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

weixin_34006468發表於2019-01-07

面試題

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

面試官心理分析

如果有人問到你 MQ 的知識,高可用是必問的上一講提到,MQ 會導致系統可用性降低。所以只要你用了 MQ,接下來問的一些要點肯定就是圍繞著 MQ 的那些缺點怎麼來解決了。

要是你傻乎乎的就幹用了一個 MQ,各種問題從來沒考慮過,那你就杯具了,面試官對你的感覺就是,只會簡單使用一些技術,沒任何思考,馬上對你的印象就不太好了。這樣的同學招進來要是做個 20k 薪資以內的普通小弟還湊合,要是做薪資 20k+ 的高工,那就慘了,讓你設計個系統,裡面肯定一堆坑,出了事故公司受損失,團隊一起背鍋。

面試題剖析

這個問題這麼問是很好的,因為不能問你 Kafka 的高可用性怎麼保證?ActiveMQ 的高可用性怎麼保證?一個面試官要是這麼問就顯得很沒水平,人家可能用的就是 RabbitMQ,沒用過 Kafka,你上來問人家 Kafka 幹什麼?這不是擺明了刁難人麼。

所以有水平的面試官,問的是 MQ 的高可用性怎麼保證?這樣就是你用過哪個 MQ,你就說說你對那個 MQ 的高可用性的理解。

RabbitMQ 的高可用性

RabbitMQ 是比較有代表性的,因為是基於主從(非分散式)做高可用性的,我們就以 RabbitMQ 為例子講解第一種 MQ 的高可用性怎麼實現。

RabbitMQ 有三種模式:單機模式、普通叢集模式、映象叢集模式。

單機模式

單機模式,就是 Demo 級別的,一般就是你本地啟動了玩玩兒的?,沒人生產用單機模式。

普通叢集模式(無高可用性)

普通叢集模式,意思就是在多臺機器上啟動多個 RabbitMQ 例項,每個機器啟動一個。你建立的 queue,只會放在一個 RabbitMQ 例項上,但是每個例項都同步 queue 的後設資料(後設資料可以認為是 queue 的一些配置資訊,通過後設資料,可以找到 queue 所在例項)。你消費的時候,實際上如果連線到了另外一個例項,那麼那個例項會從 queue 所在例項上拉取資料過來。

325120-cb814f847cf067ae.png
mq-7

這種方式確實很麻煩,也不怎麼好,沒做到所謂的分散式,就是個普通叢集。因為這導致你要麼消費者每次隨機連線一個例項然後拉取資料,要麼固定連線那個 queue 所在例項消費資料,前者有資料拉取的開銷,後者導致單例項效能瓶頸

而且如果那個放 queue 的例項當機了,會導致接下來其他例項就無法從那個例項拉取,如果你開啟了訊息持久化,讓 RabbitMQ 落地儲存訊息的話,訊息不一定會丟,得等這個例項恢復了,然後才可以繼續從這個 queue 拉取資料。

所以這個事兒就比較尷尬了,這就沒有什麼所謂的高可用性這方案主要是提高吞吐量的,就是說讓叢集中多個節點來服務某個 queue 的讀寫操作。

映象叢集模式(高可用性)

這種模式,才是所謂的 RabbitMQ 的高可用模式。跟普通叢集模式不一樣的是,在映象叢集模式下,你建立的 queue,無論後設資料還是 queue 裡的訊息都會存在於多個例項上,就是說,每個 RabbitMQ 節點都有這個 queue 的一個完整映象,包含 queue 的全部資料的意思。然後每次你寫訊息到 queue 的時候,都會自動把訊息同步到多個例項的 queue 上。

325120-8c010f7e54467673.png
mq-8

那麼如何開啟這個映象叢集模式呢?其實很簡單,RabbitMQ 有很好的管理控制檯,就是在後臺新增一個策略,這個策略是映象叢集模式的策略,指定的時候是可以要求資料同步到所有節點的,也可以要求同步到指定數量的節點,再次建立 queue 的時候,應用這個策略,就會自動將資料同步到其他的節點上去了。

這樣的話,好處在於,你任何一個機器當機了,沒事兒,其它機器(節點)還包含了這個 queue 的完整資料,別的 consumer 都可以到其它節點上去消費資料。壞處在於,第一,這個效能開銷也太大了吧,訊息需要同步到所有機器上,導致網路頻寬壓力和消耗很重!第二,這麼玩兒,不是分散式的,就沒有擴充套件性可言了,如果某個 queue 負載很重,你加機器,新增的機器也包含了這個 queue 的所有資料,並沒有辦法線性擴充套件你的 queue。你想,如果這個 queue 的資料量很大,大到這個機器上的容量無法容納了,此時該怎麼辦呢?

Kafka 的高可用性

Kafka 一個最基本的架構認識:由多個 broker 組成,每個 broker 是一個節點;你建立一個 topic,這個 topic 可以劃分為多個 partition,每個 partition 可以存在於不同的 broker 上,每個 partition 就放一部分資料。

這就是天然的分散式訊息佇列,就是說一個 topic 的資料,是分散放在多個機器上的,每個機器就放一部分資料

實際上 RabbmitMQ 之類的,並不是分散式訊息佇列,它就是傳統的訊息佇列,只不過提供了一些叢集、HA(High Availability, 高可用性) 的機制而已,因為無論怎麼玩兒,RabbitMQ 一個 queue 的資料都是放在一個節點裡的,映象叢集下,也是每個節點都放這個 queue 的完整資料。

Kafka 0.8 以前,是沒有 HA 機制的,就是任何一個 broker 當機了,那個 broker 上的 partition 就廢了,沒法寫也沒法讀,沒有什麼高可用性可言。

比如說,我們假設建立了一個 topic,指定其 partition 數量是 3 個,分別在三臺機器上。但是,如果第二臺機器當機了,會導致這個 topic 的 1/3 的資料就丟了,因此這個是做不到高可用的。

325120-2810932de1ce6cc5.png
kafka-before

Kafka 0.8 以後,提供了 HA 機制,就是 replica(複製品) 副本機制。每個 partition 的資料都會同步到其它機器上,形成自己的多個 replica 副本。所有 replica 會選舉一個 leader 出來,那麼生產和消費都跟這個 leader 打交道,然後其他 replica 就是 follower。寫的時候,leader 會負責把資料同步到所有 follower 上去,讀的時候就直接讀 leader 上的資料即可。只能讀寫 leader?很簡單,要是你可以隨意讀寫每個 follower,那麼就要 care 資料一致性的問題,系統複雜度太高,很容易出問題。Kafka 會均勻地將一個 partition 的所有 replica 分佈在不同的機器上,這樣才可以提高容錯性。

325120-9b9b11db05bea140.png
kafka-after

這麼搞,就有所謂的高可用性了,因為如果某個 broker 當機了,沒事兒,那個 broker上面的 partition 在其他機器上都有副本的,如果這上面有某個 partition 的 leader,那麼此時會從 follower 中重新選舉一個新的 leader 出來,大家繼續讀寫那個新的 leader 即可。這就有所謂的高可用性了。

寫資料的時候,生產者就寫 leader,然後 leader 將資料落地寫本地磁碟,接著其他 follower 自己主動從 leader 來 pull 資料。一旦所有 follower 同步好資料了,就會傳送 ack 給 leader,leader 收到所有 follower 的 ack 之後,就會返回寫成功的訊息給生產者。(當然,這只是其中一種模式,還可以適當調整這個行為)

消費的時候,只會從 leader 去讀,但是隻有當一個訊息已經被所有 follower 都同步成功返回 ack 的時候,這個訊息才會被消費者讀到。

看到這裡,相信你大致明白了 Kafka 是如何保證高可用機制的了,對吧?不至於一無所知,現場還能給面試官畫畫圖。要是遇上面試官確實是 Kafka 高手,深挖了問,那你只能說不好意思,太深入的你沒研究過。

轉載自:網際網路 Java 工程師進階知識完全掃盲,萬分感謝。

更多資訊,關注java架構

325120-52aaabb3179e50ec.jpeg
WechatIMG136.jpeg

相關文章