1. 程式人生 > >為什麼分散式一定要有訊息佇列?

為什麼分散式一定要有訊息佇列?

1、為什麼要使用訊息佇列?

分析:一個用訊息佇列的人,不知道為啥用,這就有點尷尬。沒有複習這點,很容易被問蒙,然後就開始胡扯了。
回答:這個問題,咱只答三個最主要的應用場景(不可否認還有其他的,但是隻答三個主要的),即以下六個字:解耦、非同步、削峰

(1)解耦

傳統模式:

傳統模式的缺點:

  • 系統間耦合性太強,如上圖所示,系統A在程式碼中直接呼叫系統B和系統C的程式碼,如果將來D系統接入,系統A還需要修改程式碼,過於麻煩!

中介軟體模式:

中介軟體模式的的優點:

  • 將訊息寫入訊息佇列,需要訊息的系統自己從訊息佇列中訂閱,從而系統A不需要做任何修改。

(2)非同步

傳統模式:

傳統模式的缺點:

  • 一些非必要的業務邏輯以同步的方式執行,太耗費時間。

中介軟體模式:

中介軟體模式的的優點:

  • 將訊息寫入訊息佇列,非必要的業務邏輯以非同步的方式執行,加快響應速度

(3)削峰

傳統模式

傳統模式的缺點:

  • 併發量大的時候,所有的請求直接懟到資料庫,造成資料庫連線異常

中介軟體模式:

中介軟體模式的的優點:

  • 系統A慢慢的按照資料庫能處理的併發量,從訊息佇列中慢慢拉取訊息。在生產中,這個短暫的高峰期積壓是允許的。

2、使用了訊息佇列會有什麼缺點?

分析:一個使用了MQ的專案,如果連這個問題都沒有考慮過,就把MQ引進去了,那就給自己的專案帶來了風險。我們引入一個技術,要對這個技術的弊端有充分的認識,才能做好預防。要記住,不要給公司挖坑!
回答:回答也很容易,從以下兩個個角度來答

  • 系統可用性降低:你想啊,本來其他系統只要執行好好的,那你的系統就是正常的。現在你非要加個訊息佇列進去,那訊息佇列掛了,你的系統不是呵呵了。因此,系統可用性降低
  • 系統複雜性增加:要多考慮很多方面的問題,比如一致性問題、如何保證訊息不被重複消費,如何保證保證訊息可靠傳輸。因此,需要考慮的東西更多,系統複雜性增大。

但是,我們該用還是要用的。

3、訊息佇列如何選型?

先說一下,博主只會ActiveMQ,RabbitMQ,RocketMQ,Kafka,對什麼ZeroMQ等其他MQ沒啥理解,因此只能基於這四種MQ給出回答。
分析:既然在專案中用了MQ,肯定事先要對業界流行的MQ進行調研,如果連每種MQ的優缺點都沒了解清楚,就拍腦袋依據喜好,用了某種MQ,還是給專案挖坑。如果面試官問:"你為什麼用這種MQ?。"你直接回答"領導決定的。"這種回答就很LOW了。還是那句話,不要給公司挖坑。
回答:首先,咱先上

ActiveMQ的社群,看看該MQ的更新頻率:

Apache ActiveMQ 5.15.3 Release
Christopher L. Shannon posted on Feb 12, 2018
Apache ActiveMQ 5.15.2 Released
Christopher L. Shannon posted on Oct 23, 2017
Apache ActiveMQ 5.15.0 Released
Christopher L. Shannon posted on Jul 06, 2017
省略以下記錄
...


我們可以看出,ActiveMq幾個月才發一次版本,據說研究重心在他們的下一代產品Apollo。
接下來,我們再去RabbitMQ的社群去看一下,RabbitMQ的更新頻率

Apache ActiveMQ 5.15.3 Release
Christopher L. Shannon posted on Feb 12, 2018
Apache ActiveMQ 5.15.2 Released
Christopher L. Shannon posted on Oct 23, 2017
Apache ActiveMQ 5.15.0 Released
Christopher L. Shannon posted on Jul 06, 2017
省略以下記錄
...

我們可以看出,RabbitMQ版本釋出比ActiveMq頻繁很多。至於RocketMQ和kafka就不帶大家看了,總之也比ActiveMQ活躍的多。詳情,可自行查閱。
再來一個效能對比表

特性 ActiveMQ RabbitMQ RocketMQ kafka
開發語言 java erlang java scala
單機吞吐量 萬級 萬級 10萬級 10萬級
時效性 ms級 us級 ms級 ms級以內
可用性 高(主從架構) 高(主從架構) 非常高(分散式架構) 非常高(分散式架構)
功能特性 成熟的產品,在很多公司得到應用;有較多的文件;各種協議支援較好 基於erlang開發,所以併發能力很強,效能極其好,延時很低;管理介面較豐富 MQ功能比較完備,擴充套件性佳 只支援主要的MQ功能,像一些訊息查詢,訊息回溯等功能沒有提供,畢竟是為大資料準備的,在大資料領域應用廣。

綜合上面的材料得出以下兩點:
(1)中小型軟體公司,建議選RabbitMQ.一方面,erlang語言天生具備高併發的特性,而且他的管理介面用起來十分方便。正所謂,成也蕭何,敗也蕭何!他的弊端也在這裡,雖然RabbitMQ是開源的,然而國內有幾個能定製化開發erlang的程式設計師呢?所幸,RabbitMQ的社群十分活躍,可以解決開發過程中遇到的bug,這點對於中小型公司來說十分重要。不考慮rocketmq和kafka的原因是,一方面中小型軟體公司不如網際網路公司,資料量沒那麼大,選訊息中介軟體,應首選功能比較完備的,所以kafka排除。不考慮rocketmq的原因是,rocketmq是阿里出品,如果阿里放棄維護rocketmq,中小型公司一般抽不出人來進行rocketmq的定製化開發,因此不推薦。
(2)大型軟體公司,根據具體使用在rocketMq和kafka之間二選一。一方面,大型軟體公司,具備足夠的資金搭建分散式環境,也具備足夠大的資料量。針對rocketMQ,大型軟體公司也可以抽出人手對rocketMQ進行定製化開發,畢竟國內有能力改JAVA原始碼的人,還是相當多的。至於kafka,根據業務場景選擇,如果有日誌採集功能,肯定是首選kafka了。具體該選哪個,看使用場景。

4、如何保證訊息佇列是高可用的?

分析:在第二點說過了,引入訊息佇列後,系統的可用性下降。在生產中,沒人使用單機模式的訊息佇列。因此,作為一個合格的程式設計師,應該對訊息佇列的高可用有很深刻的瞭解。如果面試的時候,面試官問,你們的訊息中介軟體如何保證高可用的?你的回答只是表明自己只會訂閱和釋出訊息,面試官就會懷疑你是不是隻是自己搭著玩,壓根沒在生產用過。請做一個愛思考,會思考,懂思考的程式設計師。
回答:這問題,其實要對訊息佇列的叢集模式要有深刻了解,才好回答。
以rcoketMQ為例,他的叢集就有多master 模式、多master多slave非同步複製模式、多 master多slave同步雙寫模式。多master多slave模式部署架構圖(網上找的,偷個懶,懶得畫):
image
其實博主第一眼看到這個圖,就覺得和kafka好像,只是NameServer叢集,在kafka中是用zookeeper代替,都是用來儲存和發現master和slave用的。通訊過程如下:
Producer 與 NameServer叢集中的其中一個節點(隨機選擇)建立長連線,定期從 NameServer 獲取 Topic 路由資訊,並向提供 Topic 服務的 Broker Master 建立長連線,且定時向 Broker 傳送心跳。Producer 只能將訊息傳送到 Broker master,但是 Consumer 則不一樣,它同時和提供 Topic 服務的 Master 和 Slave建立長連線,既可以從 Broker Master 訂閱訊息,也可以從 Broker Slave 訂閱訊息。
那麼kafka呢,為了對比說明直接上kafka的拓補架構圖(也是找的,懶得畫)
image
如上圖所示,一個典型的Kafka叢集中包含若干Producer(可以是web前端產生的Page View,或者是伺服器日誌,系統CPU、Memory等),若干broker(Kafka支援水平擴充套件,一般broker數量越多,叢集吞吐率越高),若干Consumer Group,以及一個Zookeeper叢集。Kafka通過Zookeeper管理叢集配置,選舉leader,以及在Consumer Group發生變化時進行rebalance。Producer使用push模式將訊息釋出到broker,Consumer使用pull模式從broker訂閱並消費訊息。
至於rabbitMQ,也有普通叢集和映象叢集模式,自行去了解,比較簡單,兩小時即懂。
要求,在回答高可用的問題時,應該能邏輯清晰的畫出自己的MQ叢集架構或清晰的敘述出來。

5、如何保證訊息不被重複消費?

分析:這個問題其實換一種問法就是,如何保證訊息佇列的冪等性?這個問題可以認為是訊息佇列領域的基本問題。換句話來說,是在考察你的設計能力,這個問題的回答可以根據具體的業務場景來答,沒有固定的答案。
回答:先來說一下為什麼會造成重複消費?
  其實無論是那種訊息佇列,造成重複消費原因其實都是類似的。正常情況下,消費者在消費訊息時候,消費完畢後,會發送一個確認資訊給訊息佇列,訊息佇列就知道該訊息被消費了,就會將該訊息從訊息佇列中刪除。只是不同的訊息佇列傳送的確認資訊形式不同,例如RabbitMQ是傳送一個ACK確認訊息,RocketMQ是返回一個CONSUME_SUCCESS成功標誌,kafka實際上有個offset的概念,簡單說一下(如果還不懂,出門找一個kafka入門到精通教程),就是每一個訊息都有一個offset,kafka消費過訊息後,需要提交offset,讓訊息佇列知道自己已經消費過了。那造成重複消費的原因?,就是因為網路傳輸等等故障,確認資訊沒有傳送到訊息佇列,導致訊息佇列不知道自己已經消費過該訊息了,再次將該訊息分發給其他的消費者。
  如何解決?這個問題針對業務場景來答分以下幾點
  (1)比如,你拿到這個訊息做資料庫的insert操作。那就容易了,給這個訊息做一個唯一主鍵,那麼就算出現重複消費的情況,就會導致主鍵衝突,避免資料庫出現髒資料。
  (2)再比如,你拿到這個訊息做redis的set的操作,那就容易了,不用解決,因為你無論set幾次結果都是一樣的,set操作本來就算冪等操作。
  (3)如果上面兩種情況還不行,上大招。準備一個第三方介質,來做消費記錄。以redis為例,給訊息分配一個全域性id,只要消費過該訊息,將<id,message>以K-V形式寫入redis。那消費者開始消費前,先去redis中查詢有沒消費記錄即可。

6、如何保證消費的可靠性傳輸?

分析:我們在使用訊息佇列的過程中,應該做到訊息不能多消費,也不能少消費。如果無法做到可靠性傳輸,可能給公司帶來千萬級別的財產損失。同樣的,如果可靠性傳輸在使用過程中,沒有考慮到,這不是給公司挖坑麼,你可以拍拍屁股走了,公司損失的錢,誰承擔。還是那句話,認真對待每一個專案,不要給公司挖坑。
回答:其實這個可靠性傳輸,每種MQ都要從三個角度來分析:生產者弄丟資料、訊息佇列弄丟資料、消費者弄丟資料

RabbitMQ

(1)生產者丟資料
從生產者弄丟資料這個角度來看,RabbitMQ提供transaction和confirm模式來確保生產者不丟訊息。
transaction機制就是說,傳送訊息前,開啟事物(channel.txSelect()),然後傳送訊息,如果傳送過程中出現什麼異常,事物就會回滾(channel.txRollback()),如果傳送成功則提交事物(channel.txCommit())。
然而缺點就是吞吐量下降了。因此,按照博主的經驗,生產上用confirm模式的居多。一旦channel進入confirm模式,所有在該通道上面釋出的訊息都將會被指派一個唯一的ID(從1開始),一旦訊息被投遞到所有匹配的佇列之後,rabbitMQ就會發送一個Ack給生產者(包含訊息的唯一ID),這就使得生產者知道訊息已經正確到達目的隊列了.如果rabiitMQ沒能處理該訊息,則會發送一個Nack訊息給你,你可以進行重試操作。處理Ack和Nack的程式碼如下所示(說好不上程式碼的,偷偷上了):

channel.addConfirmListener(new ConfirmListener() {  
                @Override  
                public void handleNack(long deliveryTag, boolean multiple) throws IOException {  
                    System.out.println("nack: deliveryTag = "+deliveryTag+" multiple: "+multiple);  
                }  
                @Override  
                public void handleAck(long deliveryTag, boolean multiple) throws IOException {  
                    System.out.println("ack: deliveryTag = "+deliveryTag+" multiple: "+multiple);  
                }  
            });  


(2)訊息佇列丟資料

處理訊息佇列丟資料的情況,一般是開啟持久化磁碟的配置。這個持久化配置可以和confirm機制配合使用,你可以在訊息持久化磁碟後,再給生產者傳送一個Ack訊號。這樣,如果訊息持久化磁碟之前,rabbitMQ陣亡了,那麼生產者收不到Ack訊號,生產者會自動重發。
那麼如何持久化呢,這裡順便說一下吧,其實也很容易,就下面兩步
1、將queue的持久化標識durable設定為true,則代表是一個持久的佇列
2、傳送訊息的時候將deliveryMode=2
這樣設定以後,rabbitMQ就算掛了,重啟後也能恢復資料
(3)消費者丟資料
消費者丟資料一般是因為採用了自動確認訊息模式。這種模式下,消費者會自動確認收到資訊。這時rahbitMQ會立即將訊息刪除,這種情況下如果消費者出現異常而沒能處理該訊息,就會丟失該訊息。
至於解決方案,採用手動確認訊息即可。

kafka

這裡先引一張kafka Replication的資料流向圖
image
Producer在釋出訊息到某個Partition時,先通過ZooKeeper找到該Partition的Leader,然後無論該Topic的Replication Factor為多少(也即該Partition有多少個Replica),Producer只將該訊息傳送到該Partition的Leader。Leader會將該訊息寫入其本地Log。每個Follower都從Leader中pull資料。
針對上述情況,得出如下分析
(1)生產者丟資料
在kafka生產中,基本都有一個leader和多個follwer。follwer會去同步leader的資訊。因此,為了避免生產者丟資料,做如下兩點配置

  1. 第一個配置要在producer端設定acks=all。這個配置保證了,follwer同步完成後,才認為訊息傳送成功。
  2. 在producer端設定retries=MAX,一旦寫入失敗,這無限重試

(2)訊息佇列丟資料
針對訊息佇列丟資料的情況,無外乎就是,資料還沒同步,leader就掛了,這時zookpeer會將其他的follwer切換為leader,那資料就丟失了。針對這種情況,應該做兩個配置。

  1. replication.factor引數,這個值必須大於1,即要求每個partition必須有至少2個副本
  2. min.insync.replicas引數,這個值必須大於1,這個是要求一個leader至少感知到有至少一個follower還跟自己保持聯絡

這兩個配置加上上面生產者的配置聯合起來用,基本可確保kafka不丟資料

(3)消費者丟資料
這種情況一般是自動提交了offset,然後你處理程式過程中掛了。kafka以為你處理好了。再強調一次offset是幹嘛的
offset:指的是kafka的topic中的每個消費組消費的下標。簡單的來說就是一條訊息對應一個offset下標,每次消費資料的時候如果提交offset,那麼下次消費就會從提交的offset加一那裡開始消費。
比如一個topic中有100條資料,我消費了50條並且提交了,那麼此時的kafka服務端記錄提交的offset就是49(offset從0開始),那麼下次消費的時候offset就從50開始消費。
解決方案也很簡單,改成手動提交即可。

ActiveMQ和RocketMQ

大家自行查閱吧

7、如何保證訊息的順序性?

分析:其實並非所有的公司都有這種業務需求,但是還是對這個問題要有所複習。
回答:針對這個問題,通過某種演算法,將需要保持先後順序的訊息放到同一個訊息佇列中(kafka中就是partition,rabbitMq中就是queue)。然後只用一個消費者去消費該佇列。
有的人會問:那如果為了吞吐量,有多個消費者去消費怎麼辦?
這個問題,沒有固定回答的套路。比如我們有一個微博的操作,發微博、寫評論、刪除微博,這三個非同步操作。如果是這樣一個業務場景,那隻要重試就行。比如你一個消費者先執行了寫評論的操作,但是這時候,微博都還沒發,寫評論一定是失敗的,等一段時間。等另一個消費者,先執行寫評論的操作後,再執行,就可以成功。
總之,針對這個問題,我的觀點是保證入隊有序就行,出隊以後的順序交給消費者自己去保證,沒有固定套路。

相關推薦

為什麼分散式一定訊息佇列

1、為什麼要使用訊息佇列? 分析:一個用訊息佇列的人,不知道為啥用,這就有點尷尬。沒有複習這點,很容易被問蒙,然後就開始胡扯了。 回答:這個問題,咱只答三個最主要的應用場景(不可否認還有其他的,但是隻答三個主要的),即以下六個字:解耦、非同步、削峰 (1)解耦 傳統模式

掃盲,為什麼分散式一定Redis?

考慮到絕大部分寫業務的程式設計師,在實際開發中使用 Redis 的時候,只會 Set Value 和 Get Value 兩個操作,對 Redis 整體缺乏一個認知。所以我斗膽以 Redis 為題材,對 Redis 常見問題做一個總結,希望能夠彌補大家的知識盲點。 本文

為什麼分散式一定redis?

1、為什麼使用redis分析:博主覺得在專案中使用redis,主要是從兩個角度去考慮:效能和併發。當然,redis還具備可以做分散式鎖等其他功能,但是如果只是為了分散式鎖這些其他功能,完全還有其他中介軟體(如zookpeer等)代替,並不是非要使用redis。因此,這個問題主要從效能和併發兩個角度去答。回答:

為什麼分散式一定redis,redis的一些優缺點

1、為什麼使用redis分析:博主覺得在專案中使用redis,主要是從兩個角度去考慮:效能和併發。當然,redis還具備可以做分散式鎖等其他功能,但是如果只是為了分散式鎖這些其他功能,完全還有其他中介軟體(如zookpeer等)代替,並不是非要使用redis。因此,這個問題主

為什麽分布式一定消息隊列?

Java 分布式 Java程序員 Java工程師 程序員 0 為什麽寫這篇文章?博主有兩位朋友分別是小A和小B: 小A,工作於傳統軟件行業(某社保局的軟件外包公司),每天工作內容就是和產品聊聊需求,改改業務邏輯。再不然就是和運營聊聊天,寫幾個SQL,生成下報表。又或者接到客服的通知,某某功

為什麽分布式一定一致性方案?

Java Java程序員 分布式 架構 0 引言為什麽寫這篇文章? 首先,緩存由於其高並發和高性能的特性,已經在項目中被廣泛使用。在讀取緩存方面,大家沒啥疑問,都是按照下圖的流程來進行業務操作。但是在更新緩存方面,對於更新完數據庫,是更新緩存呢,還是刪除緩存。又或者是先刪除緩存,再更新數據庫,

為什麽微服務一定網關?

分布式 Java 架構 Java程序員 一、什麽是服務網關 服務網關 = 路由轉發 + 過濾器 1、路由轉發:接收一切外界請求,轉發到後端的微服務上去; 2、過濾器:在服務網關中可以完成一系列的橫切功能,例如權限校驗、限流以及監控等,這些都可以通過過濾器完成(其實路由轉發也是通過過濾器實現的)

為什麽分布式一定Redis?

查找 情況 超過 切換 做了 數據庫 基礎 dia ESS 本文圍繞以下幾點進行闡述: 為什麽使用 Redis 使用 Redis 有什麽缺點 單線程的 Redis 為什麽這麽快 Redis 的數據類型,以及每種數據類型的使用場景 Redis 的過期

研發哲學第五條:一定後備方案

鄭昀 20181109 #哲學 #災備 #devop   過去的九月和十月,厄運接踵而至: 大大小小連續幾次事故。 阿里雲華北機房網路抖動。 網某銀行支付通道抖動。 銀聯支付通道抖動。 某IDC機房出網流量丟包嚴重長達幾十分鐘。   我冷眼旁觀   我

程式猿的血淚史:一定資料備份的思想,不然死都不知道咋死的!!!

最近幾天水逆過頭了,硬碟在發生了眾多小毛病後,今天它終於被我搞掛了,600多G的資料,雖然真正有用的也就一點,但之前裝的軟體,配的環境什麼的一下全沒了,這感覺還是很爽啊!!!!QAQ!!! 硬碟掛掉的原因很簡單:首先它出現了一點小問題: 聯想Y700筆記本:硬碟突然不見了。好不容易

在社會上混靠的是什麼?信用!你一定一張信用卡

  很多人都認為信用卡是一個大坑,其實你誤會了信用卡了!很多人認為只有缺錢了,走投無路的人才會去辦信用卡,這真的是第一大誤解!   首先,走投無路的人是無法通過稽核的,這一個誤解可以著實的解除;其次,不一定是缺錢才會辦信用卡,信用卡是一種融資手段、理財工具,能運用資金槓桿賺取更多利差。這就

openstack中為何使用訊息佇列

1. 解耦 在專案啟動之初來預測將來專案會碰到什麼需求,是極其困難的。訊息佇列在處理過程中間插入了一個隱含的、基於資料的介面層,兩邊的處理過程都要實現這一介面。這允許你獨立的擴充套件或修改兩邊的處理過

學習java一定耐心才行!

  1.欲速則不達,初學者請不要被新技術迷惑,先把基礎學紮實。 JAVA私塾的基礎入門教材完全是老師自已編寫,配合有視訊講解,大家可以免費索取,並不是它說了多少知識,多少api.它只說了更多的“基礎知識”   2.要紮紮實實,一步一個腳印的逐步學習,不要想著一步登天。給自己

頁面框架一定路由嗎?

在現有的JS框架中,包括anguar、react、VUE等都會有路由的概念,且一定要先進行路由設定。 這個是必要的嗎?web本身就是一個很靈活的技術,個人覺得完全沒有必要使用一套固定的路由限制頁面的開發 這是我在做專案時的一個想法,後面看如何去實現。

訊息中介軟體系列第1講:為什麼訊息佇列

訊息佇列中介軟體可以說是Java開發中最常使用的一塊技術了,基本上上了規模的系統都會使用訊息佇列來優化系統架構。那麼為什麼要使用訊息佇列?我們使用訊息佇列來解決什麼問題呢? 訊息佇列的優點 對於大多數系統來說,我們使用訊息佇列來做下面三件事情:解耦、削峰、非同步。[1] 第一個作用:解耦。 在多個系統

入行數據科學一定研究生學歷嗎?

info 需要 創業公司 ron 是把 ras 而不是 算法 最好 作者 | Jeremie Harris 翻譯 | Mika CDA 數據分析師原創作品,轉載需授權 首先我要說的是,我是一名博士肄業生。 這個頭銜給我帶來了所謂的光環,它暗示我在研究生院待過,做過一些

任正非:向谷歌學習,殺出一條血路(谷歌軍團的編制不大,戰鬥力極強。任何產品都是架構第一。算法團隊直接殺入到項目中去,一線既算法又有數據,就容易突破,抽象的平臺一定要有實踐東西檢驗。藍軍不一定學歷的。我們幫助他們在工作中完成學歷教育)

工程 研究院 就是 激活 敢於 所在 邊緣計算 老師 約束 IT之家1月22日消息 日前,華為心聲社區公布了任正非在1月9日在杭州研究所業務匯報會上的講話。在本次講話中,任正非表示在軟件工程能力提升上,我們最缺乏的是對宏觀架構有清醒認識的人才。建立公司是一個架構;建立公司的

C語言一定main函式碼

眾所周知,main()函式是C程式的入口,但是C語言程式一定要有main()函式嗎,答案是肯定的,但是大家應該都見過沒有main()函式的C語言檔案,這是怎麼回事呢? 例如在Unix程式設計中,一個完

類宣告結束一定分號,否則

1>d:\myworkfiles\c++project\learn929\learn929\manfunction.cpp(4) : error C2533: “Man::{ctor}”: 建構