消息中間件的應用場景


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

不管是無限的橫向擴展服務器,還是縱向阻隔到達數據庫的流量,都是這個思路。阻隔直達數據庫的流量,緩存組件和消息組件是兩大殺器。這里就重點說說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有優勢的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM