微服務(三)

微服務(三)

Spring Cloud Bus 消息總線

在微服務架構系統中,通常會使用輕量級的消息代理來構建一個公用的消息主題 ,讓系統中所有微服務實例都連接起來。由於該主題中產生的消息都會被各個監聽實例消費,因此稱爲消息總線。消息總線可用用於配置信息的變更和統一操作管理,它的使用範圍很廣,也是微服務架構中的必備組件,通常可以配合spring cloud config 實現微服務應用配置信息的動態更新。
spring cloud bus架構
在這裏插入圖片描述
在我目前看來,rabbitMQ就是用來動態更新配置信息。
在這裏插入圖片描述
1、config(分佈式配置中心):更改配置信息
2、actuator(監控中心): 監控配置信息、手動調用 client1的actuator/bus-refresh方法,刷新client1中配置參數
3、bus (消息總線):client1發現配置參數有變、將消息發送到消息總線,消息總線收到消息後將此消息散發到所有client,client將更新配置,而不需要再手動去調用actuator中的refresh方法

Rabbit MQ

首先來了解兩個東西:
MQ:Message Queue 消息隊列,是應用程序和應用程序之間的通信方法
AMQP:Advanced Message Queuing Protocol 高級消息隊列協議
Erlang:面向併發的編程語言
一共有五種常用隊列:
在這裏插入圖片描述
work模式:能者多勞
一個生產者只能被多個消費者中的一個消費,消費掉就沒了
publish/subscribe訂閱模式:
訂閱模式
解讀:
1、1個生產者,多個消費者
2、每一個消費者都有自己的一個隊列
3、生產者沒有將消息直接發送到隊列,而是發送到了交換機
4、每個隊列都要綁定到交換機
5、生產者發送的消息,經過交換機,到達隊列,實現,一個消息被多個消費者獲取的目的
注意:一個消費者隊列可以有多個消費者實例,只有其中一個消費者實例會消費
Routing 路由模式:
在這裏插入圖片描述
主題模式:
在這裏插入圖片描述

到底什麼時候該用MQ?

並不是什麼時候有可以用MQ的、MQ作爲互聯網分層架構中的解耦器,可以進行“邏輯解耦+物理解耦”
使用場景:上下游消息通信服務,上游發送消息到MQ、下游通過MQ接收消息,上下游不存在邏輯依賴關係或者說調用方不是實時離開執行結果的業務場景可以使用MQ來進行解耦
在這裏插入圖片描述

什麼時候使用MQ?

場景一:數據驅動的任務依賴
什麼是任務依賴,舉個栗子,互聯網公司經常在凌晨進行一些數據統計任務,這些任務之間有一定的依賴關係,比如:

1)task3需要使用task2的輸出作爲輸入

2)task2需要使用task1的輸出作爲輸入

這樣的話,tast1, task2, task3之間就有任務依賴關係,必須task1先執行,再task2執行,載task3執行。
在這裏插入圖片描述
對於這類需求,常見的實現方式是,使用cron人工排執行時間表:

1)task1,0:00執行,經驗執行時間爲50分鐘

2)task2,1:00執行(爲task1預留10分鐘buffer),經驗執行時間也是50分鐘

3)task3,2:00執行(爲task2預留10分鐘buffer)

這種方法的壞處是:

1)如果有一個任務執行時間超過了預留buffer的時間,將會得到錯誤的結果,因爲後置任務不清楚前置任務是否執行成功,此時要手動重跑任務,還有可能要調整排班表

2)總任務的執行時間很長,總是要預留很多buffer,如果前置任務提前完成,後置任務不會提前開始

3)如果一個任務被多個任務依賴,這個任務將會稱爲關鍵路徑,排班表很難體現依賴關係,容易出錯

4)如果有一個任務的執行時間要調整,將會有多個任務的執行時間要調整 
在這裏插入圖片描述
優化方案是,採用MQ解耦:

1)task1準時開始,結束後發一個“task1 done”的消息

2)task2訂閱“task1 done”的消息,收到消息後第一時間啓動執行,結束後發一個“task2 done”的消息

3)task3同理

採用MQ的優點是:

1)不需要預留buffer,上游任務執行完,下游任務總會在第一時間被執行

2)依賴多個任務,被多個任務依賴都很好處理,只需要訂閱相關消息即可

3)有任務執行時間變化,下游任務都不需要調整執行時間

需要特別說明的是,MQ只用來傳遞上游任務執行完成的消息,並不用於傳遞真正的輸入輸出數據。

場景二:上游不關心執行結果
上游需要關注執行結果時要用“調用”,上游不關注執行結果時,就可以使用MQ了。

舉個栗子,58同城的很多下游需要關注“用戶發佈帖子”這個事件,比如招聘用戶發佈帖子後,招聘業務要獎勵58豆,房產用戶發佈帖子後,房產業務要送2個置頂,二手用戶發佈帖子後,二手業務要修改用戶統計數據。
在這裏插入圖片描述
對於這類需求,常見的實現方式是,使用調用關係:

帖子發佈服務執行完成之後,調用下游招聘業務、房產業務、二手業務,來完成消息的通知,但事實上,這個通知是否正常正確的執行,帖子發佈服務根本不關注。

這種方法的壞處是:

1)帖子發佈流程的執行時間增加了

2)下游服務當機,可能導致帖子發佈服務受影響,上下游邏輯+物理依賴嚴重

3)每當增加一個需要知道“帖子發佈成功”信息的下游,修改代碼的是帖子發佈服務,這一點是最噁心的,屬於架構設計中典型的依賴倒轉,誰用過誰痛誰知道(採用此法的請評論留言) 在這裏插入圖片描述
優化方案是,採用MQ解耦:

1)帖子發佈成功後,向MQ發一個消息

2)哪個下游關注“帖子發佈成功”的消息,主動去MQ訂閱

採用MQ的優點是:

1)上游執行時間短

2)上下游邏輯+物理解耦,除了與MQ有物理連接,模塊之間都不相互依賴

3)新增一個下游消息關注方,上游不需要修改任何代碼

場景三:上游關注執行結果,但執行結果很長
有時候上游需要關注執行結果,但執行結果時間很長(典型的是調用離線處理,或者跨公網調用),也經常使用回調網關+MQ來解耦。
舉個栗子,微信支付,跨公網調用微信的接口,執行時間會比較長,但調用方又非常關注執行結果,此時一般怎麼玩呢?
在這裏插入圖片描述
一般採用“回調網關+MQ”方案來解耦:

1)調用方直接跨公網調用微信接口

2)微信返回調用成功,此時並不代表返回成功

3)微信執行完成後,回調統一網關

4)網關將返回結果通知MQ

5)請求方收到結果通知

這裏需要注意的是,不應該由回調網關來調用上游來通知結果,如果是這樣的話,每次新增調用方,回調網關都需要修改代碼,仍然會反向依賴,使用回調網關+MQ的方案,新增任何對微信支付的調用,都不需要修改代碼啦。

消息總線的必達

保證消息的必達就是爲了防止消息重複發送所引發的問題.
消息必達在架構設計上需要關注的點:
消息收到先落地 (先存入數據庫)
消息超時 重傳 確認 保證消息必達
在這裏插入圖片描述
上圖是一個MQ的核心架構圖,基本可以分爲三大塊:
(1)發送方 -> 左側粉色部分
(2)MQ核心集羣 -> 中間藍色部分
(3)接收方 -> 右側黃色部分
粉色發送方又由兩部分構成:業務調用方與MQ-client-sender
其中後者向前者提供了兩個核心API:
SendMsg(bytes[] msg)
SendCallback()
藍色MQ核心集羣又分爲四個部分:MQ-server,zk,db,管理後臺web
黃色接收方也由兩部分構成:業務接收方與MQ-client-receiver
其中後者向前者提供了兩個核心API:
RecvCallback(bytes[] msg)
SendAck()

MQ是一個系統間解耦的利器,它能夠很好的解除發佈訂閱者之間的耦合,它將上下游的消息投遞解耦成兩個部分,如上述架構圖中的1箭頭和2箭頭:

(1)發送方將消息投遞給MQ,上半場

(2)MQ將消息投遞給接收方,下半場

四、MQ消息可靠投遞核心流程

MQ既然將消息投遞拆成了上下半場,爲了保證消息的可靠投遞,上下半場都必須儘量保證消息必達。

在這裏插入圖片描述

MQ消息投遞上半場,MQ-client-sender到MQ-server流程見上圖1-3:

(1)MQ-client將消息發送給MQ-server(此時業務方調用的是API:SendMsg)

(2)MQ-server將消息落地,落地後即爲發送成功

(3)MQ-server將應答發送給MQ-client(此時回調業務方是API:SendCallback)

MQ消息投遞下半場,MQ-server到MQ-client-receiver流程見上圖4-6:

(1)MQ-server將消息發送給MQ-client(此時回調業務方是API:RecvCallback)

(2)MQ-client回覆應答給MQ-server(此時業務方主動調用API:SendAck)

(3)MQ-server收到ack,將之前已經落地的消息刪除,完成消息的可靠投遞
如果消息丟了怎麼辦?

MQ消息投遞的上下半場,都可以出現消息丟失,爲了降低消息丟失的概率,MQ需要進行超時和重傳。
上半場的超時與重傳

MQ上半場的1或者2或者3如果丟失或者超時,MQ-client-sender內的timer會重發消息,直到期望收到3,如果重傳N次後還未收到,則SendCallback回調發送失敗,需要注意的是,這個過程中MQ-server可能會收到同一條消息的多次重發。
下半場的超時與重傳

MQ下半場的4或者5或者6如果丟失或者超時,MQ-server內的timer會重發消息,直到收到5並且成功執行6,這個過程可能會重發很多次消息,一般採用指數退避的策略,先隔x秒重發,2x秒重發,4x秒重發,以此類推,需要注意的是,這個過程中MQ-client-receiver也可能會收到同一條消息的多次重發。
MQ-client與MQ-server如何進行消息去重,如何進行架構冪等性設計,下一次撰文另述,此處暫且認爲爲了保證消息必達,可能收到重複的消息。

消息總線的冪等

上半場

MQ-client生成inner-msg-id,保證上半場冪等。

這個ID全局唯一,業務無關,由MQ保證。

下半場

業務發送方帶入biz-id,業務接收方去重保證冪等。

這個ID對單業務唯一,業務相關,對MQ透明。

結論:冪等性,不僅對MQ有要求,對業務上下游也有要求。

延遲消息

環形隊列是一個實現“延時消息”的好方法,開源的MQ好像都不支持延遲消息,不妨自己實現一個簡易的“延時消息隊列”,能解決很多業務問題,並減少很多低效掃庫的cron任務。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章