商城網(wǎng)站都有什么功能模塊免費(fèi)網(wǎng)站推廣工具
使用Go開發(fā)web服務(wù)時(shí)很多情況下都會(huì)使用號(hào)稱比標(biāo)準(zhǔn)庫(kù)快10x的FastHttp, 但fasthttp(版本: 20180529.0.0)至今也沒有提供優(yōu)雅關(guān)閉的方法,默認(rèn)情況下退出服務(wù)只能kill。下面談幾個(gè)實(shí)現(xiàn)方案。
首先,要明確所謂的優(yōu)雅關(guān)閉是要求我們?cè)谡{(diào)用close()
時(shí)要做到以下幾點(diǎn):
- 拒絕接受新連接
- 等待正在處理的請(qǐng)求完成,然后關(guān)閉連接
- 關(guān)閉剩余空閑的連接
要做到第一點(diǎn),我們需要重寫一下net.Listener
實(shí)現(xiàn),例如叫GraceListener
, 在此結(jié)構(gòu)體中組合一個(gè)真正干活的Listener,覆蓋Close()
方法,在此方法中先將干活的Listener關(guān)閉,此時(shí)就不會(huì)再監(jiān)聽新請(qǐng)求了,然后再block當(dāng)前routine直到所有連接全部關(guān)閉為止。代碼如下:
type graceListener struct {net.Listener
}func (gl *graceListener) Close() error {err := gl.Close()if nil != err {return err}// block, 直到所有連接關(guān)閉
}
fastServ := &fasthttp.Server{Concurrency: 100,Handler: xxxFunc,LogAllErrors: true,}ln, err := net.Listen("tcp4", ":8080")if nil != err {// err}graceLn := &graceListener{Listener: ln,}fastServ.Serve(graceLn)
接下來(lái)看看如何滿足后面兩條要求。這里有兩種方案,第一種最簡(jiǎn)單的方案是,保存一個(gè)全局的sync.WaitGroup
指針,在你的請(qǐng)求處理函數(shù)中,先調(diào)用wg.Add(1)
, 然后defer wg.Done()
, 最后在上面的Close()
方法中使用wg.Wait()
即可。這里建議最好使用select
給等待加個(gè)超時(shí)功能,即如果超過(guò)指定時(shí)間還沒有退出則強(qiáng)制退出:
// 此方法一直block到所有請(qǐng)求退出或超時(shí)
func WaitForGracefullyClose() error {select {case <-waitAllRoutineDone():return nilcase <-time.After(maxWait):return fmt.Errorf("force shutdown after %v", maxWait)}}// 等待所有請(qǐng)求處理routine完成;
// 此方法返回只有1個(gè)緩沖的channel, 只有當(dāng)所有routine結(jié)束時(shí)channel才會(huì)有元素
func waitAllRoutineDone() chan struct{} {flagChan := make(chan struct{}, 1)go func() {wg.Wait()flagChan <- struct{}{}}()return flagChan
}
此外還有第二種方案,那就是在自己的graceListener
中添加一個(gè)計(jì)數(shù)器用于統(tǒng)計(jì)當(dāng)前的連接數(shù),重寫Accept()
方法,將計(jì)數(shù)器+1,再定義一個(gè)自己的套殼net.Conn
實(shí)現(xiàn),重寫Close()
方法,在里面將計(jì)數(shù)器-1。這里要注意線程安全問題,最好使用atomic
包進(jìn)行操作。最后在graceListener#Close()
中關(guān)閉Listener后等待計(jì)數(shù)器歸零。這種方案有以下幾個(gè)缺點(diǎn):
- 實(shí)現(xiàn)繁瑣
- 無(wú)法處理keep-alive連接。即請(qǐng)求已經(jīng)處理完成,但是連接并沒有關(guān)閉,這時(shí)計(jì)數(shù)器不會(huì)歸零。不過(guò)可以使用go 1.3新增加的
Conn State Hook
來(lái)實(shí)現(xiàn)當(dāng)連接狀態(tài)變更時(shí)的通知,但是也是比較繁瑣的。
因此不推薦這種方案。
完成Http Server的關(guān)閉后,就可以添加一些清理自己業(yè)務(wù)資源的邏輯了,比如關(guān)閉數(shù)據(jù)庫(kù)連接,redis連接,取消注冊(cè),刷新日志等。