背景
我們的服務(wù)器大致架構(gòu)是一個(gè)接入服mixer,mixer會(huì)連后臺(tái)的服務(wù)backend,兩者之間通過(guò)GRPC通信(長(zhǎng)連接)。backend可能有多實(shí)例,所以為了負(fù)載均衡,mixer維護(hù)了一個(gè)連接池,連接池會(huì)定期進(jìn)行刷新(2min一次)。
問(wèn)題
當(dāng)某一個(gè)backend實(shí)例重啟時(shí),mixer并不能立即感知到,而要等到下一次刷新連接池才會(huì)更新連接。這樣在backend重啟到mixer下次刷新期間的請(qǐng)求如果被路由到正在重啟的backend實(shí)例,就會(huì)一直等到超時(shí)返回。
解決
我們是通過(guò)kubernetes管理服務(wù),backend重啟時(shí),k8s會(huì)先啟動(dòng)一個(gè)新的實(shí)例,等新的實(shí)例ready for serve,就發(fā)送SIGTERM給舊的實(shí)例。在接收到SIGTERM后,backend就進(jìn)入關(guān)閉過(guò)程,這時(shí)是無(wú)法保證正常返回的。
所以在backend程序中加了對(duì)系統(tǒng)SIGTERM信號(hào)的處理:當(dāng)接收到SIGTERM信號(hào),會(huì)設(shè)置一個(gè)全局變量為true,標(biāo)識(shí)服務(wù)器正在關(guān)閉中。當(dāng)其他線程接收到mixer的信號(hào),會(huì)去讀這個(gè)全局變量,發(fā)現(xiàn)自己已經(jīng)在關(guān)閉過(guò)程中,就會(huì)給mixer返回一個(gè)特定的消息。
那么mixer接收到這個(gè)消息應(yīng)該如何做呢?
1)接收到這個(gè)消息代表某個(gè)backend實(shí)例即將失效,應(yīng)該對(duì)連接池中所有指向這個(gè)實(shí)例的連接進(jìn)行刷新。
2)接收到這個(gè)消息的請(qǐng)求應(yīng)該換一個(gè)有效的連接進(jìn)行重試。
但是這里有個(gè)問(wèn)題就是我們用的是GRPC,進(jìn)行連接的是一個(gè)k8s service的地址,而不是每個(gè)instance的真實(shí)地址,所以我們維護(hù)的連接池中的連接,是不知道自己連向哪一個(gè)backend實(shí)例的,所以1)和2)都會(huì)有問(wèn)題,如下:
1)無(wú)法區(qū)分哪些連接是指向即將關(guān)閉的實(shí)例,所以就無(wú)法確定該刷新哪些連接。
2)無(wú)法知道哪個(gè)連接是指向即將關(guān)閉的實(shí)例,所以無(wú)法選出有效的連接。
針對(duì)1),最簡(jiǎn)單的辦法就是在收到SIGTERM時(shí)刷新全部連接,但代價(jià)太高,會(huì)阻塞mixer的全部服務(wù)。
針對(duì)2),如何選到一個(gè)有效的連接呢?由于待重啟的實(shí)例在接收到SIGTERM信號(hào)時(shí),k8s已經(jīng)將其從service的列表中摘除,那么刷新后的連接就肯定不是連向即將關(guān)閉的實(shí)例,問(wèn)題解決。