消息隊列在高併發的場景是必備技能,隨着咱們的使用,在生產環境中的問題也是很是的多,好比:消息隊列如何作到高可用呢?
場景的中間件有不少種類型,在這裏就準備經常使用的一些就用於分析處理。網絡
RabbitMQ 是比較有表明性的,由於是基於主從(非分佈式)作高可用性的,咱們就以 RabbitMQ 爲例子講解第一種 MQ 的高可用性怎麼實現。架構
RabbitMQ 有三種模式:單機模式、普通集羣模式、鏡像集羣模式。併發
單機模式,就是 Demo 級別的,通常就是你本地啓動了玩玩兒的,沒人生產用單機模式。分佈式
普通集羣模式,意思就是在多臺機器上啓動多個 RabbitMQ 實例,每一個機器啓動一個。你建立的 queue,只會放在一個 RabbitMQ 實例上,可是每一個實例都同步 queue 的元數據(元數據能夠認爲是 queue 的一些配置信息,經過元數據,能夠找到 queue 所在實例)。你消費的時候,實際上若是鏈接到了另一個實例,那麼那個實例會從 queue 所在實例上拉取數據過來。高併發
這種方式確實很麻煩,也不怎麼好,沒作到所謂的分佈式,就是個普通集羣。由於這致使你要麼消費者每次隨機鏈接一個實例而後拉取數據,要麼固定鏈接那個 queue 所在實例消費數據,前者有數據拉取的開銷,後者致使單實例性能瓶頸。性能
並且若是那個放 queue 的實例宕機了,會致使接下來其餘實例就沒法從那個實例拉取,若是你開啓了消息持久化,讓 RabbitMQ 落地存儲消息的話,消息不必定會丟,得等這個實例恢復了,而後才能夠繼續從這個 queue 拉取數據。中間件
因此這個事兒就比較尷尬了,這就沒有什麼所謂的高可用性,這方案主要是提升吞吐量的,就是說讓集羣中多個節點來服務某個 queue 的讀寫操做。隊列
這種模式,纔是所謂的 RabbitMQ 的高可用模式。跟普通集羣模式不同的是,在鏡像集羣模式下,你建立的 queue,不管元數據仍是 queue 裏的消息都會存在於多個實例上,就是說,每一個 RabbitMQ 節點都有這個 queue 的一個完整鏡像,包含 queue 的所有數據的意思。而後每次你寫消息到 queue 的時候,都會自動把消息同步到多個實例的 queue 上。同步
那麼如何開啓這個鏡像集羣模式呢?其實很簡單,RabbitMQ 有很好的管理控制檯,就是在後臺新增一個策略,這個策略是鏡像集羣模式的策略,指定的時候是能夠要求數據同步到全部節點的,也能夠要求同步到指定數量的節點,再次建立 queue 的時候,應用這個策略,就會自動將數據同步到其餘的節點上去了。消息隊列
這樣的話,好處在於,你任何一個機器宕機了,沒事兒,其它機器(節點)還包含了這個 queue 的完整數據,別的 consumer 均可以到其它節點上去消費數據。壞處在於,第一,這個性能開銷也太大了吧,消息須要同步到全部機器上,致使網絡帶寬壓力和消耗很重!第二,這些玩兒,不是分佈式的,就沒有擴展性可言了,若是某個 queue 負載很重,你加機器,新增的機器也包含了這個 queue 的全部數據,並沒有辦法線性擴展你的 queue。
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 的數據就丟了,所以這個是作不到高可用的。
Kafka 0.8 之後,提供了 HA 機制,就是 replica(複製品) 副本機制。每一個 partition 的數據都會同步到其它機器上,造成本身的多個 replica 副本。全部 replica 會選舉一個 leader 出來,那麼生產和消費都跟這個 leader 打交道,而後其餘 replica 就是 follower。寫的時候,leader 會負責把數據同步到全部 follower 上去,讀的時候就直接讀 leader 上的數據便可。只能讀寫 leader?很簡單,要是你能夠隨意讀寫每一個 follower,那麼就要 care 數據一致性的問題,系統複雜度過高,很容易出問題。Kafka 會均勻地將一個 partition 的全部 replica 分佈在不一樣的機器上,這樣才能夠提升容錯性。
這麼搞,就有所謂的高可用性了,由於若是某個 broker 宕機了,沒事兒,那個 broker上面的 partition 在其餘機器上都有副本的。若是這個宕機的 broker 上面有某個 partition 的 leader,那麼此時會從 follower 中從新選舉一個新的 leader 出來,你們繼續讀寫那個新的 leader 便可。這就有所謂的高可用性了。
寫數據的時候,生產者就寫 leader,而後 leader 將數據落地寫本地磁盤,接着其餘 follower 本身主動從 leader 來 pull 數據。一旦全部 follower 同步好數據了,就會發送 ack 給 leader,leader 收到全部 follower 的 ack 以後,就會返回寫成功的消息給生產者。(固然,這只是其中一種模式,還能夠適當調整這個行爲)
消費的時候,只會從 leader 去讀,可是隻有當一個消息已經被全部 follower 都同步成功返回 ack 的時候,這個消息纔會被消費者讀到。