一、引言
在通信系統(tǒng)或者多系統(tǒng)協(xié)同的軟件系統(tǒng)里,網(wǎng)元或系統(tǒng)之間經(jīng)常需要進行信息同步或者信息查詢。系統(tǒng)間的消息查詢一般為長鏈接,如果遇到網(wǎng)絡(luò)異;蛘邔Χ讼到y(tǒng)處理不過來的情況,會在短時間內(nèi)產(chǎn)生大量的查詢消息。反復查詢反復失敗,前面失敗的消息以及新到達的查詢消息大量積壓,反復的查詢和大量的消息處理使得系統(tǒng)性能急劇下降,甚至影響到其他無關(guān)的業(yè)務。因此,為保障系統(tǒng)的穩(wěn)定可靠,對于異常情況的處理至關(guān)重要。尤其是適用“5個9”要求的通信系統(tǒng),導致服務出問題的往往是一些異常情況,需要有充足的邏輯來保障出現(xiàn)異常情況時,系統(tǒng)仍然能夠穩(wěn)定、可靠地處理業(yè)務。
下面我們就沿著是什么,怎么做,為什么這樣做的思路逐步討論分析。
二、是什么產(chǎn)生網(wǎng)絡(luò)信息波動
如上圖,我們看到大部分業(yè)務的開展都是三方系統(tǒng)之間的服務應用在交互,這樣在很大程度上就有多種因素會導致消息傳輸?shù)牟▌樱陨蠘I(yè)務的邏輯,我將它歸納為兩點一線。
兩點指的是起始端和目的端,一線指的是傳輸介質(zhì):
1)兩點間的服務中斷或者異常就會導致信息傳輸?shù)牟豢蛇_,直接導致信息的丟失,造成業(yè)務失敗。
2)傳輸介質(zhì)的網(wǎng)絡(luò)質(zhì)量不行,例如丟包率過大,也會導致信息傳輸異常,造成業(yè)務失敗。
基于以上兩點,讓我們大致了解業(yè)務中網(wǎng)絡(luò)信息波動產(chǎn)生的原因,接下來我們闡述一下該如何應對。
三、怎么做降低信息傳輸失敗的概率
1)設(shè)計方案
針對系統(tǒng)間信息同步時的異常情況,基于消息隊列+Redis緩存,采用退避算法,實現(xiàn)了消息延遲消費。當出現(xiàn)網(wǎng)絡(luò)波動或者對端網(wǎng)元響應緩慢時,失敗的消息按照退避算法進行合理退讓。這樣,不僅減少了消息的無效嘗試,降低了無效的系統(tǒng)開銷,而且保障了其他正常業(yè)務的順利運行,確保了即使在網(wǎng)絡(luò)波動或者對端系統(tǒng)回復延遲的異常情況下,也不會因為大量堆積的查詢消息消耗過多的系統(tǒng)資源,系統(tǒng)性能的穩(wěn)定可靠得到保障。
2)技術(shù)架構(gòu)
(1)消息通過Kafka或其他消息中間件進入正常的消費隊列進行生產(chǎn)和消費,如果都成功,則完成信息處理。
(2)消息如果在經(jīng)過Kafka等消息中間件發(fā)生異常,根據(jù)消息失敗的情況,根據(jù)規(guī)避算法,計算出該消息需要延遲的時間,寫入消息的延遲時間里,將消息按照一定的格式(sortedSet)存儲到Redis中。
(3)通過每秒級別的定時輪詢,通過reverseRangeByScore獲取到消息。根據(jù)消息的延遲時間判斷該消息是否達到消費時間,如果達到消費時間,將再次進入kafka的中間件進入重試消費隊列進行生產(chǎn)和消費。如果沒有達到消費時間,繼續(xù)排隊,優(yōu)先處理其他延遲時間已到的消息。這樣的機制減少了無效的消息重試,保障了隊列中其他消息能夠得到及時處理。
(4)此外,系統(tǒng)在高并發(fā)的情況下,會繼續(xù)產(chǎn)生大量查詢消息。為避免隊列中的消息堆積,消息的重復消費具有次數(shù)限制。根據(jù)測試,3次重試是一個有效的保障值,如果3次重試還是沒有響應,該消息將落庫DB數(shù)據(jù)庫,不再占用隊列資源。最后定時任務會每小時對DB里的堆積消息進行輪詢,輪詢到的消息會再次進入Kafka的重試隊列進行消費。
四、為什么這樣做,好處是什么?
(1)從業(yè)務上講:
從圖一,我們可知我們對接的業(yè)務都是彼此分離的三方業(yè)務系統(tǒng),在產(chǎn)生網(wǎng)絡(luò)信息波動的情況下,我們從兩點一線中能盡快解決的就是屬于自己業(yè)務服務側(cè)的一點,其他的一點和傳輸介質(zhì)都需要耗費較大的人力去溝通解決。我們需要盡可能地將消費失敗的信息傳輸?shù)饺,但同時也要保證自己服務的消息不被積壓,致使自己系統(tǒng)的服務崩潰,以上架構(gòu)很好地解決了磁力問題
(2)從先進性上講:
本方案的消息中間件探索了一條在高并發(fā)場景下,減少堆積消息的有效路徑。在傳統(tǒng)設(shè)計模式下,系統(tǒng)間的查詢消息均是等間隔地進行查詢,在網(wǎng)絡(luò)穩(wěn)定和并發(fā)量低的情況下,這樣的技術(shù)方案可以表現(xiàn)穩(wěn)定。但是一旦網(wǎng)絡(luò)波動或者并發(fā)量加大,等間隔的消息查詢會造成大量的堆積消息,從而影響到系統(tǒng)的穩(wěn)定性和可靠性。在解決堆積消息方面,網(wǎng)上較多的方案是進行隊列的消息預警,這樣的解決方案相當于一種事后補救方案。但是本方案消息中間件從消息的源頭進行有效的算法設(shè)計,保障了從源頭上減少無效的重復消息。這樣的解決方案彌補了主流技術(shù)設(shè)計上的不足,是一種比較先進的設(shè)計思路,填補了公司在處理高并發(fā)、高穩(wěn)定性要求的業(yè)務上的技術(shù)空白。
(3)從兼容性上講:
在本架構(gòu)中,采用了多種技術(shù)的組合設(shè)計,不拘泥于某一種特定的技術(shù),F(xiàn)在市場上流行的消息中間件如:ActiveMQ、RabbitMQ、Kafka、RocketMQ、ZeroMQ等,都可以在架構(gòu)中使用。本方案消息中間件的架構(gòu)設(shè)計具有很強大的兼容性。
(4)從靈活性上講:
在本架構(gòu)中,消息的延時消費等級可以根據(jù)自己項目的需要自由設(shè)定,消息的延時消費次數(shù)也可以根據(jù)自己項目的需要自由設(shè)定,系統(tǒng)靈活性強。
(5)從可復用性上講:
本消息中間件采用常用的消息隊列+Redis緩存,是一種可復用性很強的消息中間件,可廣泛應用于各類高并發(fā)、高可靠性要求的業(yè)務系統(tǒng)。
(6)從性能的角度講:
redis可以作為一個高性能的存儲db,性能要比MySQL好很多,并且支持持久化,穩(wěn)定性好。redis SortedSet隊列天然支持以時間作為條件排序,完美滿足我們選出要推送記錄的需要。為保障消息均能得到及時處理,一次從隊列中取出執(zhí)行的數(shù)量可以設(shè)置得大一點或啟動多個推送的服務。假設(shè)一次從隊列中取出執(zhí)行的數(shù)量設(shè)置為:2000,推送的服務節(jié)點為:3個,定時任務執(zhí)行間隔為:1秒,一分鐘內(nèi)可以實際推送的數(shù)量為:2000 * 60 * 3 = 360000(理想情況下:多個推送隊列subscribe-queue的推送任務分布均勻)。這個數(shù)量已經(jīng)可以滿足絕大部分的需求。
五、總結(jié)
本文探討的方案是從三方系統(tǒng)的消息穩(wěn)定性出發(fā)設(shè)計的,其實這一套設(shè)計也可適用于本系統(tǒng)多個模塊的營運服務,隨著技術(shù)的迭代更新,相信會有更好、更加完善的解決方案問世。本文方案僅做拋磚引玉,希望相關(guān)問題能夠得到大家更多的關(guān)注和討論,期待各位提供更好的思路和建議,謝謝大家。