RabbitMQ在分布式系統(tǒng)中的應用


由于之前做的項目中需要在多個節(jié)點之間可靠地通信沮峡,所以廢棄了之前使用的Redis pub/sub(因為集群有單點問題,且有諸多限制),改用了RabbitMQ。
使用期間得到不少收獲责语,也踩了不少坑炮障,所以在此分享下心得目派。

1 - 怎么保證可靠性的?

RabbitMQ提供了幾種特性胁赢,犧牲了一點性能代價企蹭,提供了可靠性的保證。

  • 持久化
    當RabbitMQ退出時,默認會將消息和隊列都清除谅摄,所以需要在第一次聲明隊列和發(fā)送消息時指定其持久化屬性為true徒河,
    這樣RabbitMQ會將隊列、消息和狀態(tài)存到RabbitMQ本地的數(shù)據(jù)庫送漠,重啟后會恢復顽照。

    durable=true
    channel.queueDeclare("task_queue", durable, false, false, null);//隊列 
    channel.basicPublish("", "task_queue", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());//消息
    

    注:當聲明的隊列已經(jīng)存在時,嘗試重新定義它的durable是不生效的闽寡。

  • 接收應答
    客戶端接收消息的模式默認是自動應答代兵,通過設置autoAck為false可以讓客戶端主動應答消息.
    當客戶端拒絕此消息或者未應答便斷開連接時,就會使得此消息重新入隊(2.7.0以前是重新加入到隊尾爷狈,2.7.0及以后是保留在隊列中的原來位置).

    autoAck = false;
    requeue = true;
    channel.basicConsume(queue, autoAck, callback);
    channel.basicAck();//應答
    channel.basicReject(deliveryTag, requeue);//拒絕
    channel.basicRecover(requeue);//恢復
    
  • 發(fā)送確認
    默認情況下植影,發(fā)送端不關注發(fā)出去的消息是否被消費掉了∠延溃可設置channel為confirm模式.
    所有發(fā)送的消息都會被確認一次思币,用戶可以自行根據(jù)server發(fā)回的確認消息查看狀態(tài)。詳細介紹見:confirms

    channel.confirmSelect(); // 進入confirm模式
    do publish messages... // 每個消息都會被編號羡微,從1開始
    channel.getNextPublishSeqNo() // 查看下一個要發(fā)送的消息的序號
    channel.waitForConfirms(); // 等待所有消息發(fā)送并確認
    
  • 事務
    和confirm模式不能同時使用谷饿,而且會帶來大量的多余開銷,導致吞吐量下降很多拷淘,故而不推薦各墨。

    channel.txSelect();
    try {
        do something...
        channel.txCommit();
    } catch (e){
        channel.txRollback();
    }
    
  • 消息隊列的高可用(主備模式)

    相比于路由和綁定,可以視為是共享于所有的節(jié)點的启涯,消息隊列默認只存在于第一次聲明它的節(jié)點上, 這樣一旦這個節(jié)點掛了
    這個隊列中未處理的消息就沒有了. 幸好贬堵,RabbitMQ提供了將它備份到其他節(jié)點的機制. 任何時候都有
    一個master負責處理請求,其他slaves負責備份结洼,當master掛掉黎做,會將最早創(chuàng)建的那個slave提升為master

    命令:rabbitmqctl set_policy ha-all “^ha\.” ‘{“ha-mode”:”all”}’
    設置所有以’ha’開頭的queue在所有節(jié)點上擁有備份。詳細語法點這里.也可以在界面上配置

    20160516071133333.png

    注:由于exclusive類型的隊列會在client和server連接斷開時被刪掉松忍,所以對它設置持久化屬性和備份都是沒有意義的

  • 順序保證

直接上圖好了:

2 - 一些需要注意的地方

  • 集群配置
    一個集群中多個節(jié)點共享一份.erlang.cookie文件蒸殿;若是沒有啟用RABBITMQ_USE_LONGNAME,需要在每個節(jié)點的hosts文件中指定其他節(jié)點的地址鸣峭,不然會找不到其他集群中的節(jié)點宏所。

  • 腦裂
    RabbitMQ集群對于網(wǎng)絡分區(qū)的處理和忍受能力不太好,推薦使用federation或者shovel插件去解決摊溶。federation詳見高級->Federation爬骤。但是,情況已經(jīng)發(fā)生了莫换,怎么去解決呢霞玄?放心骤铃,還是有辦法恢復的。當網(wǎng)絡斷斷續(xù)續(xù)時坷剧,會使得節(jié)點之間的通信斷掉惰爬,進而造成集群被分隔開的情況。這樣惫企,每個小集群之后便只處理各自本地的連接和消息撕瞧,從而導致數(shù)據(jù)不同步。當重新恢復網(wǎng)絡連接時狞尔,它們彼此都認為是對方掛了-_-||风范,便可以判斷出有網(wǎng)絡分區(qū)出現(xiàn)了。但是RabbitMQ默認是忽略掉不處理的沪么,造成兩個節(jié)點繼續(xù)各自為政(路由硼婿,綁定關系,隊列等可以獨立地創(chuàng)建刪除禽车,甚至主備隊列也會每一方擁有自己的master)寇漫。可以更改配置使得連接恢復時殉摔,會根據(jù)配置自動恢復州胳。

    1. ignore:默認,不做任何處理
    2. pause-minority:斷開連接時逸月,判斷當前節(jié)點是否屬于少數(shù)派(節(jié)點數(shù)少于或者等于一半)栓撞,如果是,則暫停直到恢復連接碗硬。
    3. {pause_if_all_down, [nodes], ignore | autoheal}:斷開連接時瓤湘,判斷當前集群中節(jié)點是否有節(jié)點在nodes中,如果有恩尾,則繼續(xù)運行弛说,否則暫停直到恢復連接。這種策略下翰意,當恢復連接時木人,可能會有多個分區(qū)存活,所以冀偶,最后一個參數(shù)決定它們怎么合并醒第。
    4. autoheal:當恢復連接時,選擇客戶端連接數(shù)最多的節(jié)點狀態(tài)為主进鸠,重啟其他節(jié)點稠曼。

    配置:**【詳見下文:集群配置】

    1. 多次ack:客戶端多次應答同一條消息,會使得該客戶端收不到后續(xù)消息堤如。

3 - 結合Docker使用

集群版本的實現(xiàn):詳見我自己寫的一個例子rabbitmq-server-cluster

4 - 消息隊列中間件的比較

  • RabbitMQ:
    1. 優(yōu)點:支持很多協(xié)議如:AMQP蒲列,XMPP,STMP搀罢,STOMP蝗岖;靈活的路由;成熟穩(wěn)定的集群方案榔至;負載均衡抵赢;數(shù)據(jù)持久化等。
    2. 缺點:速度較慢唧取;比較重量級铅鲤,安裝需要依賴Erlang環(huán)境。
  • Redis:
    1. 優(yōu)點:比較輕量級枫弟,易上手
    2. 缺點:單點問題邢享,功能單一
  • Kafka:
    1. 優(yōu)點:高吞吐;分布式淡诗;快速持久化骇塘;負載均衡;輕量級
    2. 缺點:極端情況下會丟消息

最后附一張網(wǎng)上截取的測試結果:

5 - 幾個重要的概念

  • Virtual Host:包含若干個Exchange和Queue韩容,表示一個節(jié)點款违;
  • Exchange:接受客戶端發(fā)送的消息,并根據(jù)Binding將消息路由給服務器中的隊列群凶,Exchange分為direct插爹、fanout、topic三種请梢;
  • Binding:連接Exchange和Queue赠尾,包含路由規(guī)則;
  • Queue:消息隊列毅弧,存儲還未被消費的消息萍虽;
  • Message:Header+Body;
  • Channel:通道形真,執(zhí)行AMQP的命令杉编;一個連接可創(chuàng)建多個通道以節(jié)省資源。

6 - Client

RabbitMQ官方實現(xiàn)了很多熱門語言的客戶端咆霜,就不一一列舉啦邓馒,以java為例,直接開始正題:

  • 建立連接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
// 可以加上斷開重試機制:
factory.setAutomaticRecoveryEnabled(true);
factory.setNetworkRecoveryInterval(10000);
  • 創(chuàng)建連接和通道
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
  • 一對一:一個生產(chǎn)者蛾坯,一個消費者

代碼同上光酣,只不過會有多個消費者,消息會輪序發(fā)給各個消費者脉课。

如果設置了autoAck=false救军,那么可以實現(xiàn)公平分發(fā)(即對于某個特定的消費者财异,每次最多只發(fā)送指定條數(shù)的消息,直到其中一條消息應答后唱遭,再發(fā)送下一條)戳寸。需要在消費者中加上:

int prefetchCount = 1;
channel.basicQos(prefetchCount);
  • 廣播

生產(chǎn)者:

channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, EXCHANGE_NAME, "");
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());

消費者同上

  • Routing

生產(chǎn)者:支持通配符的Routing

String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, EXCHANGE_NAME, routingKey);
channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes());

消費者同上

原文鏈接

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拷泽,隨后出現(xiàn)的幾起案子疫鹊,更是在濱河造成了極大的恐慌,老刑警劉巖司致,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拆吆,死亡現(xiàn)場離奇詭異,居然都是意外死亡脂矫,警方通過查閱死者的電腦和手機枣耀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來庭再,“玉大人奕枢,你說我怎么就攤上這事∨逦ⅲ” “怎么了缝彬?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長哺眯。 經(jīng)常有香客問我谷浅,道長,這世上最難降的妖魔是什么奶卓? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任一疯,我火速辦了婚禮,結果婚禮上夺姑,老公的妹妹穿的比我還像新娘墩邀。我一直安慰自己,他們只是感情好盏浙,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布眉睹。 她就那樣靜靜地躺著,像睡著了一般废膘。 火紅的嫁衣襯著肌膚如雪竹海。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天丐黄,我揣著相機與錄音斋配,去河邊找鬼。 笑死,一個胖子當著我的面吹牛艰争,可吹牛的內(nèi)容都是我干的坏瞄。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼甩卓,長吁一口氣:“原來是場噩夢啊……” “哼鸠匀!你這毒婦竟也來了?” 一聲冷哼從身側響起猛频,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛛勉,沒想到半個月后鹿寻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡诽凌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年毡熏,在試婚紗的時候發(fā)現(xiàn)自己被綠了媚污。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杨凑。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡否过,死狀恐怖鸥诽,靈堂內(nèi)的尸體忽然破棺而出沃于,到底是詐尸還是另有隱情饺饭,我是刑警寧澤贴届,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布慕购,位于F島的核電站躬络,受9級特大地震影響尖奔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜穷当,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一提茁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧馁菜,春花似錦茴扁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至智嚷,卻和暖如春躲胳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纤勒。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工坯苹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人摇天。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓粹湃,卻偏偏與公主長得像恐仑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子为鳄,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理裳仆,服務發(fā)現(xiàn),斷路器孤钦,智...
    卡卡羅2017閱讀 134,716評論 18 139
  • 關于消息隊列歧斟,從前年開始斷斷續(xù)續(xù)看了些資料,想寫很久了偏形,但一直沒騰出空静袖,近來分別碰到幾個朋友聊這塊的技術選型,是時...
    預流閱讀 584,969評論 51 786
  • 來源 RabbitMQ是用Erlang實現(xiàn)的一個高并發(fā)高可靠AMQP消息隊列服務器俊扭。支持消息的持久化队橙、事務、擁塞控...
    jiangmo閱讀 10,369評論 2 34
  • 本文章翻譯自http://www.rabbitmq.com/api-guide.html萨惑,并沒有及時更新捐康。 術語對...
    joyenlee閱讀 7,671評論 0 3
  • 在writeToFile 方法中 atomically,參數(shù)值為BOOL類型庸蔼,用于通知是否應該首先將文件內(nèi)容保存在...
    執(zhí)著的小蛋撻閱讀 654評論 0 1