訊息中介軟體的應用場景

kopoo發表於2021-04-22

提高系統效能首先考慮的是資料庫的優化,但是資料庫因為歷史原因,橫向擴充套件是一件非常複雜的工程,所有我們一般會盡量把流量都擋在資料庫之前。

不管是無限的橫向擴充套件伺服器,還是縱向阻隔到達資料庫的流量,都是這個思路。阻隔直達資料庫的流量,快取元件和訊息元件是兩大殺器。這裡就重點說說MQ的應用場景。

 

MQ簡介

MQ:Message queue,訊息佇列,就是指儲存訊息的一個容器。具體的定義這裡就不類似於資料庫、快取等,用來儲存資料的。當然,與資料庫、快取等產品比較,也有自己一些特點,具體的特點後文會做詳細的介紹。

現在常用的MQ元件有activeMQ、rabbitMQ、rocketMQ、zeroMQ,當然近年來火熱的kafka,從某些場景來說,也是MQ,當然kafka的功能更加強大,雖然不同的MQ都有自己的特點和優勢,但是,不管是哪種MQ,都有MQ本身自帶的一些特點,下面,我們們就先聊聊MQ的特點。

 

MQ特點

(1)先進先出
不能先進先出,都不能說是佇列了。訊息佇列的順序在入隊的時候就基本已經確定了,一般是不需人工干預的。而且,最重要的是,資料是隻有一條資料在使用中。 這也是MQ在諸多場景被使用的原因。

(2)釋出訂閱
釋出訂閱是一種很高效的處理方式,如果不發生阻塞,基本可以當做是同步操作。這種處理方式能非常有效的提升伺服器利用率,這樣的應用場景非常廣泛。

(3)持久化
持久化確保MQ的使用不只是一個部分場景的輔助工具,而是讓MQ能像資料庫一樣儲存核心的資料。

(4)分散式
在現在大流量、大資料的使用場景下,只支援單體應用的伺服器軟體基本是無法使用的,支援分散式的部署,才能被廣泛使用。而且,MQ的定位就是一個高效能的中介軟體。

 

應用場景

訊息佇列中介軟體是分散式系統中重要的元件,主要解決應用解耦,非同步訊息,流量削鋒等問題,實現高效能,高可用,可伸縮和最終一致性架構。目前使用較多的訊息佇列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ

 

訊息中介軟體監控

Activemq 監控

Rabbitmq 監控

Kafka 監控

 

非同步處理

場景說明:使用者註冊後,需要發註冊郵件和註冊簡訊。傳統的做法有兩種 1.序列的方式;2.並行方式
a、序列方式:將註冊資訊寫入資料庫成功後,傳送註冊郵件,再傳送註冊簡訊。以上三個任務全部完成後,返回給客戶端。

b、並行方式:將註冊資訊寫入資料庫成功後,傳送註冊郵件的同時,傳送註冊簡訊。以上三個任務完成後,返回給客戶端。與序列的差別是,並行的方式可以提高處理的時間

假設三個業務節點每個使用50毫秒鐘,不考慮網路等其他開銷,則序列方式的時間是150毫秒,並行的時間可能是100毫秒。
因為CPU在單位時間內處理的請求數是一定的,假設CPU1秒內吞吐量是100次。則序列方式1秒內CPU可處理的請求量是7次(1000/150)。並行方式處理的請求量是10次(1000/100)
小結:如以上案例描述,傳統的方式系統的效能(併發量,吞吐量,響應時間)會有瓶頸。如何解決這個問題呢?

引入訊息佇列,將不是必須的業務邏輯,非同步處理。改造後的架構如下:

按照以上約定,使用者的響應時間相當於是註冊資訊寫入資料庫的時間,也就是50毫秒。註冊郵件,傳送簡訊寫入訊息佇列後,直接返回,因此寫入訊息佇列的速度很快,基本可以忽略,因此使用者的響應時間可能是50毫秒。因此架構改變後,系統的吞吐量提高到每秒20 QPS。比序列提高了3倍,比並行提高了兩倍。

 

應用解耦

場景說明:使用者下單後,訂單系統需要通知庫存系統。傳統的做法是,訂單系統呼叫庫存系統的介面。如下圖:

傳統模式的缺點:假如庫存系統無法訪問,則訂單減庫存將失敗,從而導致訂單失敗,訂單系統與庫存系統耦合

如何解決以上問題呢?引入應用訊息佇列後的方案,如下圖:

訂單系統:使用者下單後,訂單系統完成持久化處理,將訊息寫入訊息佇列,返回使用者訂單下單成功
庫存系統:訂閱下單的訊息,採用拉/推的方式,獲取下單資訊,庫存系統根據下單資訊,進行庫存操作
假如:在下單時庫存系統不能正常使用。也不影響正常下單,因為下單後,訂單系統寫入訊息佇列就不再關心其他的後續操作了。實現訂單系統與庫存系統的應用解耦

流量削峰

流量削鋒也是訊息佇列中的常用場景,一般在秒殺或團搶活動中使用廣泛。
應用場景:秒殺活動,一般會因為流量過大,導致流量暴增,應用掛掉。為解決這個問題,一般需要在應用前端加入訊息佇列。
a、可以控制活動的人數
b、可以緩解短時間內高流量壓垮應用

 使用者的請求,伺服器接收後,首先寫入訊息佇列。假如訊息佇列長度超過最大數量,則直接拋棄使用者請求或跳轉到錯誤頁面。

秒殺業務根據訊息佇列中的請求資訊,再做後續處理。

 

訊息通訊

訊息通訊是指,訊息佇列一般都內建了高效的通訊機制,因此也可以用在純的訊息通訊。比如實現點對點訊息佇列,或者聊天室等。
點對點通訊:

 客戶端A和客戶端B使用同一佇列,進行訊息通訊。

聊天室通訊:

客戶端A,客戶端B,客戶端N訂閱同一主題,進行訊息釋出和接收。實現類似聊天室效果。

以上實際是訊息佇列的兩種訊息模式,點對點或釋出訂閱模式。模型為示意圖,供參考。

具體例子可以參考官網:https://www.rabbitmq.com/web-stomp.html

 

海量資料同步(日誌)

日誌處理是指將訊息佇列用在日誌處理中,比如Kafka的應用,解決大量日誌傳輸的問題。架構簡化如下

日誌採集客戶端,負責日誌資料採集,定時寫受寫入Kafka佇列
Kafka訊息佇列,負責日誌資料的接收,儲存和轉發
日誌處理應用:訂閱並消費kafka佇列中的日誌資料 

 

eg:日誌收集系統

分為Zookeeper註冊中心,日誌收集客戶端,Kafka叢集和Storm叢集(OtherApp)四部分組成。
Zookeeper註冊中心,提出負載均衡和地址查詢服務
日誌收集客戶端,用於採集應用系統的日誌,並將資料推送到kafka佇列
Kafka叢集:接收,路由,儲存,轉發等訊息處理
Storm叢集:與OtherApp處於同一級別,採用拉的方式消費佇列中的資料

 

網易使用案例:

網易NDC-DTS系統在使用,應該是最典型的應用場景,主要就是binlog的同步,資料表的主從複製。簡單一點就是:MySQL程式寫binlog檔案 -> 同步應用去實時監控binlog檔案讀取傳送到Kafka -> 目標端處理binlog  。原理上與阿里開源的canal, 點評的puma大同小異。

 

任務排程

參考延時佇列

 

分散式事物

分散式事務有強一致,弱一致,和最終一致性這三種:

強一致:

當更新操作完成之後,任何多個後續程式或者執行緒的訪問都會返回最新的更新過的值。這種是對使用者最友好的,就是使用者上一次寫什麼,下一次就保證能讀到什麼。根據 CAP 理論,這種實現需要犧牲可用性。

 

弱一致:

系統並不保證續程式或者執行緒的訪問都會返回最新的更新過的值。系統在資料寫入成功之後,不承諾立即可以讀到最新寫入的值,也不會具體的承諾多久之後可以讀到。

 

最終一致:

弱一致性的特定形式。系統保證在沒有後續更新的前提下,系統最終返回上一次更新操作的值。在沒有故障發生的前提下,不一致視窗的時間主要受通訊延遲,系統負載和複製副本的個數影響。DNS 是一個典型的最終一致性系統。

 

在分散式系統中,同時滿足“CAP定律”中的“一致性”、“可用性”和“分割槽容錯性”三者是幾乎不可能的。在網際網路領域的絕大多數的場景,都需要犧牲強一致性來換取系統的高可用性,系統往往只需要保證“最終一致性”,只要這個最終時間是在使用者可以接受的範圍內即可,這時候我們只需要用短暫的資料不一致就可以達到我們想要效果。

 

例項描述

比如有訂單,庫存兩個資料,一個下單過程簡化為,加一個訂單,減一個庫存。 而訂單和庫存是獨立的服務,那怎麼保證資料一致性。

這時候我們需要思考一下,怎麼保證兩個遠端呼叫“同時成功”,資料一致?

請大家先注意一點遠端呼叫最鬱悶的地方就是,結果有3種,成功、失敗和超時。 超時的話,成功失敗都有可能。

一般的解決方案,大多數的做法是藉助mq來做最終一致。

 

如何實現最終一致

我們是怎麼利用Mq來達到最終一致的呢?

 

首先,拿我們上面提到的訂單業務舉例:

在我們進行加訂單的過程中同時插入logA(這個過程是可以做本地事務的)

然後可以非同步讀取logA,發mqA

B端接收mqA,同時減少庫存,B這裡需要做冪等(避免因為重複訊息造成的業務錯亂)

 

那麼我們通過上面的分析可能聯想到這樣的問題?

本地先執行事務,執行成功了就發個訊息過去,消費端拿到訊息執行自己的事務
比如a,b,c,a非同步呼叫b,c如果b失敗了,或者b成功,或者b超時,那麼怎麼用mq讓他們最終一致呢?b失敗就失敗了,b成功之後給c發一個訊息,b和c對a來講都是非同步的,且他們都是同時進行的話,而且需要a,b,c同時成功的情況,那麼這種情況用mq怎麼做?

其實做法還是參照於本地事務的概念的。

第一種情況:假設a,b,c三者都正常執行,那整個業務正常結束

第二種情況:假設b超時,那麼需要a給b重發訊息(記得b服務要做冪等),如果出現重發失敗的話,需要看情況,是終端服務,還是繼續重發,甚至人為干預(所有的規則制定都需要根據業務規則來定)

第三種情況:假設a,b,c三者之中的一個失敗了,失敗的服務利用MQ給其他的服務傳送訊息,其他的服務接收訊息,查詢本地事務記錄日誌,如果本地也失敗,刪除收到的訊息(表示訊息消費成功),如果本地成功的話,則需要呼叫補償介面進行補償(需要每個服務都提供業務補償介面)。


注意事項:

mq這裡有個坑,通常只適用於只允許第一個操作失敗的場景,也就是第一個成功之後必須保證後面的操作在業務上沒障礙,不然後面失敗了前面不好回滾,只允許系統異常的失敗,不允許業務上的失敗,通常業務上失敗一次後面基本上也不太可能成功了,要是因為網路或當機引起的失敗可以通過重試解決,如果業務異常,那就只能發訊息給a和c讓他們做補償了吧?通常是通過第三方進行補償,ABC提供補償介面,設計正規化裡通常不允許消費下游業務失敗。


怎麼理解呢,舉個例子:
比如A給B轉賬,A先自己扣錢,然後發了個訊息,B這邊如果在這之前銷戶了,那重試多少次也沒用,只能人工干預。

網易在分散式事務採用的解決方式

網易部分業務是用MQ實現了最終一致性,目前教育產品,例如:網易雲課堂。

也有一部分業務用了TCC事務,但是TCC事務用的比較少,因為會侵染業務,開發成本比較高,如果體量不大的話直接用JPA或MQ支援事務就好。

 

網易的產品中使用分散式事務基於技術

TCC,FMT(Framework-managed transactions),事務訊息都有。

 

開源產品myth:https://gitee.com/shuaiqiyu/myth

 

常用訊息佇列(ActiveMQ、RabbitMQ、RocketMQ、Kafka)比較

特性MQ

ActiveMQ

RabbitMQ

RocketMQ

Kafka

生產者消費者模式

支援

支援

支援

支援

釋出訂閱模式

支援

支援

支援

支援

請求回應模式

支援

支援

不支援

不支援

Api完備性

多語言支援

支援

支援

java

支援

單機吞吐量

萬級

萬級

萬級

十萬級

訊息延遲

微秒級

毫秒級

毫秒級

可用性

高(主從)

高(主從)

非常高(分散式)

非常高(分散式)

訊息丟失

理論上不會丟失

理論上不會丟失

文件的完備性

教高

提供快速入門

社群活躍度

商業支援

商業雲

商業雲


ActiveMQ 歷史悠久的開源專案,已經在很多產品中得到應用,實現了JMS1.1規範,可以和spring-jms輕鬆融合,實現了多種協議,不夠輕巧(原始碼比RocketMQ多),支援持久化到資料庫,對佇列數較多的情況支援不好。總體來說:

  • RabbitMQ 它比Kafka成熟,支援AMQP事務處理,在可靠性上,RabbitMQ超過Kafka,在效能方面超過ActiveMQ。
  • RocketMQ RocketMQ是阿里開源的訊息中介軟體,目前在Apache孵化,使用純Java開發,具有高吞吐量、高可用性、適合大規模分散式系統應用的特點。RocketMQ思路起源於Kafka,但並不是簡單的複製,它對訊息的可靠傳輸及事務性做了優化,目前在阿里集團被廣泛應用於交易、充值、流計算、訊息推送、日誌流式處理、binglog分發等場景,支撐了阿里多次雙十一活動。 因為是阿里內部從實踐到產品的產物,因此裡面很多介面、API並不是很普遍適用。其可靠性毋庸置疑,而且與Kafka一脈相承(甚至更優),效能強勁,支援海量堆積。
  • Kafka設計的初衷就是處理日誌的,不支援AMQP事務處理,可以看做是一個日誌系統,針對性很強,所以它並沒有具備一個成熟MQ應該具備的特性。Kafka的效能(吞吐量、tps)比RabbitMQ要強,如果用來做大資料量的快速處理是比RabbitMQ有優勢的。

 

  • 新增到短語集
     
    • 沒有此單詞集:英語 → 中文(簡體)...
       
    • 建立新的單詞集...
  • 拷貝

相關文章