W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
分布式系統(tǒng)中,很多業(yè)務(wù)場(chǎng)景都需要考慮消息投遞的時(shí)序,例如:
(1)單聊消息投遞,保證發(fā)送方發(fā)送順序與接收方展現(xiàn)順序一致
(2)群聊消息投遞,保證所有接收方展現(xiàn)順序一致
(3)充值支付消息,保證同一個(gè)用戶發(fā)起的請(qǐng)求在服務(wù)端執(zhí)行序列一致
消息時(shí)序是分布式系統(tǒng)架構(gòu)設(shè)計(jì)中非常難的問題,ta為什么難,有什么常見優(yōu)化實(shí)踐,是本文要討論的問題。
為什么分布式環(huán)境下,消息的時(shí)序難以保證,這邊簡(jiǎn)要分析了幾點(diǎn)原因:
【時(shí)鐘不一致】
多服務(wù)器不能用“本地時(shí)間”進(jìn)行比較,假設(shè)只有一個(gè)接收方,能否用接收方本地時(shí)間表示時(shí)序呢?遺憾的是,由于多個(gè)客戶端的存在,即使是一臺(tái)服務(wù)器的本地時(shí)間,也無法表示“絕對(duì)時(shí)序”。
如上圖,絕對(duì)時(shí)序上,APP1先發(fā)出msg1,APP2后發(fā)出msg2,都發(fā)往服務(wù)器web1,網(wǎng)絡(luò)傳輸是不能保證msg1一定先于msg2到達(dá)的,所以即使以一臺(tái)服務(wù)器web1的時(shí)間為準(zhǔn),也不能精準(zhǔn)描述msg1與msg2的絕對(duì)時(shí)序。
多發(fā)送方不能保證時(shí)序,假設(shè)只有一個(gè)發(fā)送方,能否用發(fā)送方的本地時(shí)間表示時(shí)序呢?遺憾的是,由于多個(gè)接收方的存在,無法用發(fā)送方的本地時(shí)間,表示“絕對(duì)時(shí)序”。
如上圖,絕對(duì)時(shí)序上,web1先發(fā)出msg1,后發(fā)出msg2,由于網(wǎng)絡(luò)傳輸及多接收方的存在,無法保證msg1先被接收到先被處理,故也無法保證msg1與msg2的處理時(shí)序。
多發(fā)送方與多接收方都難以保證絕對(duì)時(shí)序,假設(shè)只有單一的發(fā)送方與單一的接收方,能否保證消息的絕對(duì)時(shí)序呢?結(jié)論是悲觀的,由于網(wǎng)絡(luò)傳輸與多線程的存在,仍然不行。
如上圖,web1先發(fā)出msg1,后發(fā)出msg2,即使msg1先到達(dá)(網(wǎng)絡(luò)傳輸其實(shí)還不能保證msg1先到達(dá)),由于多線程的存在,也不能保證msg1先被處理完。
【怎么保證絕對(duì)時(shí)序】
通過上面的分析,假設(shè)只有一個(gè)發(fā)送方,一個(gè)接收方,上下游連接只有一條連接池,通過阻塞的方式通訊,難道不能保證先發(fā)出的消息msg1先處理么?
回答:可以,但吞吐量會(huì)非常低,而且單發(fā)送方單接收方單連接池的假設(shè)不太成立,高并發(fā)高可用的架構(gòu)不會(huì)允許這樣的設(shè)計(jì)出現(xiàn)。
【以客戶端或者服務(wù)端的時(shí)序?yàn)闇?zhǔn)】
多客戶端、多服務(wù)端導(dǎo)致“時(shí)序”的標(biāo)準(zhǔn)難以界定,需要一個(gè)標(biāo)尺來衡量時(shí)序的先后順序,可以根據(jù)業(yè)務(wù)場(chǎng)景,以客戶端或者服務(wù)端的時(shí)間為準(zhǔn),例如:
(1)郵件展示順序,其實(shí)是以客戶端發(fā)送時(shí)間為準(zhǔn)的,潛臺(tái)詞是,發(fā)送方只要將郵件協(xié)議里的時(shí)間調(diào)整為1970年或者2970年,就可以在接收方收到郵件后一直“置頂”或者“置底”
(2)秒殺活動(dòng)時(shí)間判斷,肯定得以服務(wù)器的時(shí)間為準(zhǔn),不可能讓客戶端修改本地時(shí)間,就能夠提前秒殺
【服務(wù)端能夠生成單調(diào)遞增的id】
這個(gè)是毋庸置疑的,不展開討論,例如利用單點(diǎn)寫db的seq/auto_inc_id肯定能生成單調(diào)遞增的id,只是說性能及擴(kuò)展性會(huì)成為潛在瓶頸。對(duì)于嚴(yán)格時(shí)序的業(yè)務(wù)場(chǎng)景,可以利用服務(wù)器的單調(diào)遞增id來保證時(shí)序。
【大部分業(yè)務(wù)能接受誤差不大的趨勢(shì)遞增id】
消息發(fā)送、帖子發(fā)布時(shí)間、甚至秒殺時(shí)間都沒有這么精準(zhǔn)時(shí)序的要求:
(1)同1s內(nèi)發(fā)布的聊天消息時(shí)序亂了
(2)同1s內(nèi)發(fā)布的帖子排序不對(duì)
(3)用1s內(nèi)發(fā)起的秒殺,由于服務(wù)器多臺(tái)之間時(shí)間有誤差,落到A服務(wù)器的秒殺成功了,落到B服務(wù)器的秒殺還沒開始,業(yè)務(wù)上也是可以接受的(用戶感知不到)
所以,大部分業(yè)務(wù),長(zhǎng)時(shí)間趨勢(shì)遞增的時(shí)序就能夠滿足業(yè)務(wù)需求,非常短時(shí)間的時(shí)序誤差一定程度上能夠接受。
關(guān)于絕對(duì)遞增id,趨勢(shì)遞增id的生成架構(gòu),詳見文章《細(xì)聊分布式ID生成方法》,此處不展開。
【利用單點(diǎn)序列化,可以保證多機(jī)相同時(shí)序】
數(shù)據(jù)為了保證高可用,需要做到進(jìn)行數(shù)據(jù)冗余,同一份數(shù)據(jù)存儲(chǔ)在多個(gè)地方,怎么保證這些數(shù)據(jù)的修改消息是一致的呢?利用的就是“單點(diǎn)序列化”:
(1)先在一臺(tái)機(jī)器上序列化操作
(2)再將操作序列分發(fā)到所有的機(jī)器,以保證多機(jī)的操作序列是一致的,最終數(shù)據(jù)是一致的
GFS(Google File System)為了保證文件的可用性,一份文件要存儲(chǔ)多份,在多個(gè)上游對(duì)同一個(gè)文件進(jìn)行寫操作時(shí),也是由一個(gè)主chunk-server先序列化寫操作,再將序列化后的操作發(fā)送給其他chunk-server,來保證冗余文件的數(shù)據(jù)一致性的。
【單對(duì)單聊天,怎么保證發(fā)送順序與接收順序一致】
單人聊天的需求,發(fā)送方A依次發(fā)出了msg1,msg2,msg3三個(gè)消息給接收方B,這三條消息能否保證顯示時(shí)序的一致性(發(fā)送與顯示的順序一致)?
回答:
(1)如果利用服務(wù)器單點(diǎn)序列化時(shí)序,可能出現(xiàn)服務(wù)端收到消息的時(shí)序?yàn)閙sg3,msg1,msg2,與發(fā)出序列不一致
(2)業(yè)務(wù)上不需要全局消息一致,只需要對(duì)于同一個(gè)發(fā)送方A,ta發(fā)給B的消息時(shí)序一致就行,常見優(yōu)化方案,在A往B發(fā)出的消息中,加上發(fā)送方A本地的一個(gè)絕對(duì)時(shí)序,來表示接收方B的展現(xiàn)時(shí)序
msg1{seq:10, receiver:B,msg:content1 }潛在問題:如果接收方B先收到msg3,msg3會(huì)先展現(xiàn),后收到msg1和msg2后,會(huì)展現(xiàn)在msg3的前面。
無論如何,是按照接收方收到時(shí)序展現(xiàn),還是按照服務(wù)端收到的時(shí)序展現(xiàn),還是按照發(fā)送方發(fā)送時(shí)序展現(xiàn),是pm需要思考的點(diǎn),技術(shù)上都能夠?qū)崿F(xiàn)(接收方按照發(fā)送時(shí)序展現(xiàn)是更合理的)。
總之,需要一桿標(biāo)尺來衡量這個(gè)時(shí)序。
【群聊消息,怎么保證各接收方收到順序一致】
群聊消息的需求,N個(gè)群友在一個(gè)群里聊,怎么保證所有群友收到的消息顯示時(shí)序一致?
回答:
(1)不能再利用發(fā)送方的seq來保證時(shí)序,因?yàn)榘l(fā)送方不單點(diǎn),時(shí)間也不一致
(2)可以利用服務(wù)器的單點(diǎn)做序列化
此時(shí)群聊的發(fā)送流程為:
(1)sender1發(fā)出msg1,sender2發(fā)出msg2
(2)msg1和msg2經(jīng)過接入集群,服務(wù)集群
(3)service層到底層拿一個(gè)唯一seq,來確定接收方展示時(shí)序
(4)service拿到msg2的seq是20,msg1的seq是30
(5)通過投遞服務(wù)講消息給多個(gè)群友,群友即使接收到msg1和msg2的時(shí)間不同,但可以統(tǒng)一按照seq來展現(xiàn)
這個(gè)方法能實(shí)現(xiàn),所有群友的消息展示時(shí)序相同。
缺點(diǎn)是,這個(gè)生成全局遞增序列號(hào)的服務(wù)很容易成為系統(tǒng)瓶頸,還有沒有進(jìn)一步的優(yōu)化方法呢?
這個(gè)方案中,service層不再需要去一個(gè)統(tǒng)一的后端拿全局seq,而是在service連接池層面做細(xì)小的改造,保證一個(gè)群的消息落在同一個(gè)service上,這個(gè)service就可以用本地seq來序列化同一個(gè)群的所有消息,保證所有群友看到消息的時(shí)序是相同的。
關(guān)于id串行化的細(xì)節(jié),可詳見《緩存與數(shù)據(jù)庫(kù)一致性問題》,此處不展開。
(1)分布式環(huán)境下,消息的有序性是很難的,原因多種多樣:時(shí)鐘不一致,多發(fā)送方,多接收方,多線程,網(wǎng)絡(luò)傳輸不確定性等
(2)要“有序”,先得有衡量“有序”的標(biāo)尺,可以是客戶端標(biāo)尺,可以是服務(wù)端標(biāo)尺
(3)大部分業(yè)務(wù)能夠接受大范圍趨勢(shì)有序,小范圍誤差;絕對(duì)有序的業(yè)務(wù),可以借助服務(wù)器絕對(duì)時(shí)序的能力
(4)單點(diǎn)序列化,是一種常見的保證多機(jī)時(shí)序統(tǒng)一的方法,典型場(chǎng)景有db主從一致,gfs多文件一致
(5)單對(duì)單聊天,只需保證發(fā)出的時(shí)序與接收的時(shí)序一致,可以利用客戶端seq
(6)群聊,只需保證所有接收方消息時(shí)序一致,需要利用服務(wù)端seq,方法有兩種,一種單點(diǎn)絕對(duì)時(shí)序,另一種id串行化
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: