一、activemq
雖然是java寫的消息隊列,但是提供Java, C, C++, C#, Ruby, Perl, Python, PHP各種客戶端,所以語言上是沒什么問題的。配置和使用,基本上是java xml這一套。同時對jms、spring之類的支持很友好。
而且因為是Java寫的,所以可以作為一個jar包,放到java項目里,用代碼啟動和配置,這個對於java開發者而言是不是相當爽?畢竟還是有些場景,需要我們把隊列放到自己項目內部,隨項目啟動而啟動的。而且,還可以類似拓展tomcat一樣,自己寫java的plugin來拓展activemq。比如說,我有10萬硬件連到mq上,這10萬設備每個都有用戶名密碼,這個時候我們可以用java寫個權限驗證,從數據庫里查這10萬用戶名密碼。
activemq支持主從復制、集群。但是集群功能看起來很弱,只有failover功能,即我連一個失敗了,可以切換到其他的broker上。這一點貌似不太科學。假設有三個broker,其中一個上面沒有consumer,但另外兩個掛了,消息會轉到這個上面來,堆積起來。看樣子activemq還在升級中。
activemq工作模型比較簡單。只有兩種模式 queue,topics 。
queue就多對一,producer往queue里發送消息,消費者從queue里取,消費一條,就從queue里移除一條。如果一個消費者消費速度不夠快怎么辦呢?在activemq里,提供messageGroup的概念,一個queue可以有多個消費者,但是他們得標記自己是一個messageGroup里的。這樣,消息會輪訓發送給每個消費者,也就是說消費者不會重復消費同一條消息。但是每條消息只被消費一次。
topics就是廣播。producer往broker發消息,每個消息包含topic。消費者訂閱感興趣的topic,所有訂閱了topic的消費者都會收到消息。當然訂閱分持久不持久。持久訂閱,當消費者斷開一會,再連上來,仍然會把沒收到的消費發過來。不持久的訂閱,斷開這段時間的消息就收不到了。
activemq支持mqtt、ssl。
二、rabbitmq
rabbitmq用erlang寫的。安裝完才10m不到,在windows上使用也非常方便,在這點上完爆了activemq,java又臭又長沒辦法啊。rabbitmq給我感覺更像oracle,功能非常強大。安裝完,也有實例的概念,可以像建數據庫一樣,建實例,建用戶划權限。同時監控系統也很好用。這些都是好處,同時也是累贅,整體上來說rabbitmq比activemq復雜太多了。
從機制上來講,rabbitmq也有queue和topic的概念,發消息的時候還要指定消息的key,這個key之后會做路由鍵用。但是,多了一個概念叫做交換器exchange。exchange有四種,direct、fanout、topic、header。也就是說,發消息給rabbitmq時,消息要有一個key,並告訴他發給哪個exchange。exchange收到之后,根據key分發或者廣播給queue。消費者是從queue里拿消息的,並接觸不到交換機。
在rabbitmq里,有各種默認行為,如果我們不指定exchange,會有個默認的direct類型的exchange,如果不指定隊列和交換器的綁定關系,默認就按消息的key綁定對應的queue。此時發一個消息,消息的key是什么,就會被默認交換器送給對應的queue。
此時,其實等同於activemq的queue模式。
在rabbitmq里,一個queue可以有多個消費者
通過設置prefetch的值為1,可以讓多個消費者每次都取到一條記錄,消費完再取下一條。這兩種都是使用direct交換器,即消息的key是多少,就把消息放到key對應的queue中。
fanout交換器。實際上就是廣播,發送到fanout交換器的消息,會被轉發給所有和這個交換器綁定的隊列。通常我們把隊列搞成臨時的,這樣就解耦了。例如用戶登錄,發送一個登陸消息到fanout交換器,同時有一個smsQueue和交換器綁定,一個消費者從這個smsQueue里取出誰登陸了,並發送一條短信。過了幾天,我們希望用戶登陸可以獲得積分。那么我再聲明一個scoreQueue綁定到這個fanout交換器,實現積分更改邏輯。下圖是fanout(X為交換器)
總體說來fanout其實就是direct交換器實現的。把兩個隊列都綁定到direct,綁定的時候指定同一個key,就變成fanout交換器了,如下圖
queue和exchange綁定的時候,也可以指定多個綁定key,這時候就實現了簡單版的訂閱。如下圖
當然這樣不夠靈活,我想要靠通配符綁定如何呢,這時候就不用direct交換器了,用topic交換器
“#”通配剩余字符,"*"通配部分字符。 如果綁定的時候key為“#”,那么其實就是fanout交換器。如果一個通配符都沒有,其實就是direct交換器。
head交換器貌似是通過消息附帶的頭信息來路由的,不過官方對這個介紹的少之又少,平時也應該沒什么人用,死信隊列貌似依賴於這個。
通過交換器的概念,rabbitmq在機制上要比activemq靈活不少。對於activemq來說,你要么是個queue的消費者,要么是個topic的訂閱者。你要同時訂閱多個topic,要自己在消費者端寫代碼來實現。在rabbitmq中,你只是queue的消費者,至於你這個queue的消息是從哪個topic來,或者從哪里直接發過來,這個和消費者沒有關系,而且queue里的消息從哪來可以在rabbitmq里動態配置。所以靈活度得到了提升。
rabbitmq同樣也支持主從復制和集群。但是rabbitmq的集群非常多樣化,而且需要至少一台機器做為磁盤節點,可以持久化queue和exchange的信息,其他的可以為內存節點。普通集群中,只有exchange,queue這些定義是分布在所有機器上的,而queue中的數據不是冗余的,比如有三台rabbitmq組成了集群,他們共享同樣的exchange,queue,但是一條消息數據落到了第一台機器上,另外兩台實際上沒有這條數據的。 對於整個集群的使用,這樣其實沒有任何問題。 但出於高可用的角度來想,還是需要完完全全的分布式集群的,萬一中間有數據這台機器掛了? rabbitmq對此也有支持,把隊列數據也冗余存到三台機器上,稱之為鏡像隊列,但性能要比普通集群低,畢竟一條消息被復制到其他機器上是耗時的事情。
rabbitmq以plugin的形式支持mqtt,和spring整合也非常簡單。
三、kafka
kafka號稱為分布式而生。和activemq以及rabbitmq這些企業級隊列而言確實更有分布式系統的優勢。
kafka中,只有topic,但是每個topic可以有很多partition分區。上圖中kafka集群由兩台機器組成。topic被分成四個分區,server1維護p0,p3。 在kafka中,每個消費者都要指出自己屬於哪個consumerGroup,每個consumer可以讀取多個partition。但是一個partition在同一個consumerGroup中,只會被一個consumer消費。以此保證不會重復消費。而且在一個partition中,消息被消費的順序是可保障的。上圖中,consumer group A 由兩個consumer組成,因此一個consumer可以消費兩個partition。如果要保證嚴格的順序性,那么就要像consumer group B一樣,每個consumer只消費一個partition。kafka和rabbitmq及activemq機制上略有區別。rabbitmq和activemq都是消費后就刪除消息,沒有重復消費的功能,而kafka 隊列中的內容按策略存儲一定時間,消費者通過指定偏移量來讀取數據。如果使用基礎api可以從任意位置讀取。kafka同時提供高級api,即kafka來維護每個消費者當前讀到什么位置了,下次再來,可以接着讀。
kafka中partition是冗余存儲的。如果一個partition不幸掛了,通過選主,馬上可以切換到另外一台機器上繼續使用。這一點上,是當之無愧的分布式隊列。相比之下,rabbitmq需要配置鏡像隊列,操作太麻煩。kafka搭建集群也是非常簡單。
kafka的優勢在於: 傳統的消息隊列只有兩種模式,要么是queue,要么是publish-subscribe發布訂閱。在queue模式中,一組消費者消費一個隊列,每條消息被發給其中一個消費者。在publish-subscribe模式中,消費被廣播給所有消費者。queue模式的好處在於,他可以把消費分發給一組消費者,從而實現消費者的規模化(scale);問題在於,這樣一個消息只能被一組消費者消費,一旦消費,消息就沒有了。publish-subscribe的好處在於,一個消息可以被多組消費者消費;問題在於,你的消費者沒法規模化,即不可能出現多個消費者訂閱同一個topic,但每個消費者只處理一部分消息(雖然這個可以通過設計topic來解決)。
kafka的設計意義在於,大家都publish-subscribe,因為我的隊列數據是不刪除的,多個subscriber可以訂閱同一個topic,但是各自想從哪讀,從哪讀,互不干擾。同時提出了consumer group的概念。每個subscriber可以是多個consumer組成的,在consumer group內部,你們自己分配,不要兩個人消費同一條數據。為了達到這種目的,一個topic里的消息,被分成多個partition,既實現了上面的想法,同時又冗余(一個partition可以冗余存儲在多台機器上),達到分布式系統的高可用效果。
kafka也支持mqtt,需要寫一個connecter。kafka還提供流式計算的功能,做數據的初步清理還是很方便的。
總體而言。我感覺kafka安裝使用最簡單,同時,如果有集群要求,那么kafka是當仁不讓的首選。尤其在海量數據,以及數據有傾斜問題的場景里,因為partition的緣故,數據傾斜問題自動解決。比如個別Topic的消息量非常大,在kafka里,調整partition數就好了。反而在rabbitmq或者activemq里,這個不好解決。
rabbitmq是功能最豐富,最完善的企業級隊列。基本沒有做不了的,就算是做類似kafka的高可用集群,也是沒問題的,不過安裝部署麻煩了點。
activemq相對來說,顯的老套了一些。不過畢竟是java寫的,在內嵌到項目中的情況下,或者是簡單場景下,還是不錯的選擇。
補充一下。在kafka中,創建一個topic是一個比較重的操作,因為是分布式的,topic要同步到其他的broker,中間還要經過zookeeper。所以kafka僅僅做mqtt的輸入是ok的,但是你需要給每個硬件推送消息,實際上不太好。這方面反倒是rabbitmq比較好,因為在rabbitmq中創建幾萬的topic是很容易的,所以是可以做到每個硬件訂閱不同的topic。