- 本文出現的代碼只是簡述,詳細代碼之後文章有涉及。
簡單隊列
RabbitMQ中的消息都只能存儲在Queue中,生產者(下圖中的P)生產消息並最終投遞到Queue中,消費者(下圖中的C)可以從Queue中獲取消息並消費。
工作隊列
即單生產者,多個消費者消費隊列的情況。
工作隊列–輪詢分發
即使一個處理快,一個處理慢,消息隊列也還是平均的分發
工作隊列–公平分發(fair dipatch)
-
消費者關閉自動應答,ack改爲手動;
每個消費者發送確認消息之前,消息隊列不發送下一個消息到消費者,一次只處理一個消息,限制發送給同一個消費者不得超過一個消息
生產者(消息隊列)
//保證一次只分發一個 int prefetchCount=1; channel.baseicQos(prefetchCount)
消費者
//保證一次只分發一個 int prefetchCount=1; channel.baseicQos(prefetchCount); channel.basicAck(envelope.getDeliveryTag(),false); boolean autoAck=false;
autoAck爲true時,爲自動確認模式,即一旦rabbitMQ將消息分發給消費者,就會從內存中刪除;這種情況下,如果正在執行的消費者線程掛了,就會丟失正在處理的消息;
autoAck爲false時,如果有一個消費者掛掉,就會交付給其他消費者,rabbitmq支持消息應答,消費者發送一個消息應答,告訴rabbitmq這個消息我已經處理完成,你可以刪了,然後rabbitmq再刪除內存中的消息。
消息應答默認是打開的。
消息的持久化
如果rabbitMq掛了,怎麼辦?
這時候,引入持久化。
//聲明隊列
boolean durable=false;
channel.queueDeclare(QUEUE_NAME,durable,false,false,null);
我們將程序中的boolean durable=false改爲true是不可以的,儘管代碼是正確的,他也不會成功運行;因爲我們已經定義了一個名爲QUEUE_NAME的隊列,這個隊列是未持久化的,rabbitmq不準重新定義(不同參數)一個已存在的隊列。
訂閱模式(Publish /Subscribe)
解讀:
- 一個生產者,多個消費者
- 每個消費者都有自己的隊列
- 生產者沒有直接把消息隊列發送到隊列,而是發送到了交換機、轉發器、exchange
- 每個隊列都要綁定到交換機上
- 生產者發送的消息,經過交換機,到達隊列,就能實現一個消息被多個消費者消息
疑問:
-
如果只聲明交換機,未聲明隊列,此時發送消息,會發現消息丟失了,消息哪去了?
交換機沒有存儲的能力,在rabbitMq中只有消息隊列有存儲的能力,這時候沒有隊列綁定到交換機上,所以數據丟失了。
Exchange(交換機、轉發器)
兩方面的操作:接收生產者的消息,同時向隊列推送消息
fanout
匿名模式(非路由模式)
//其中第二個參數就爲:routingKey
channel.basicPublish(EXCHANGE_NAME,"",null,msg.getBytes())
它會把所有發送到該Exchange的消息路由到所有與它綁定的Queue中。
Routing
路由模式
Direct
exchangeType爲direct,它會把消息路由到那些binding key與routing key完全匹配的Queue中。
Topic exchange
將路由鍵和某模式匹配。
前面講到direct類型的Exchange路由規則是完全匹配binding key與routing key,但這種嚴格的匹配方式在很多情況下不能滿足實際業務需求。topic類型的Exchange在匹配規則上進行了擴展,它與direct類型的Exchage相似,也是將消息路由到binding key與routing key相匹配的Queue中,但這裏的匹配規則有些不同,它約定:
- routing key爲一個句點號“. ”分隔的字符串(我們將被句點號“. ”分隔開的每一段獨立的字符串稱爲一個單詞),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”
- binding key與routing key一樣也是句點號“. ”分隔的字符串
- binding key中可以存在兩種特殊字符“*”與“#”,用於做模糊匹配,其中“ * ”用於匹配一個單詞,“ # ”用於匹配多個單詞(可以是零個)
(商品:發佈、刪除、修改…)
RabbitMQ的消息確認機制(事務+confirm)
在rabbitmq中可以通過持久化數據,解決rabbitmq服務器異常的數據丟失問題。
問題:生產者將消息發送出去之後,消息到底有沒有到達rabbitmq服務器,默認的情況是不知道的。(之前持久化是解決了:開啓自動確認模式時,消費者突然斷電的清空)。
兩種方式:
- AMQP實現了事務機制
- Confirm模式
事務機制
txSelect、txCommit、txRollback
txSelect:用戶將當前channel設置成transation模式
txCommit:用於提交事務
txRollback:回滾事務
channel.TxSelect();//將信道設置爲事務模式
try
{
//do something
var message = Encoding.UTF8.GetBytes("TestMsg");
channel.BasicPublish("normalExchange", "NormalRoutingKey", true, null, message);
//do something
channel.TxCommit();//提交事務
}
catch (Exception ex)
{
//log(ex);
channel.TxRollback();
}
事務確實能夠解決消息發送方和RabbitMQ之間消息確認的問題,只有消息成功被RabbitMQ接收,事務才能提交成功,否則便可在捕獲異常之後進行事務回滾,與此同時可以進行消息重發。但是使用事務同樣會帶來一些問題。
- 會阻塞,發佈者必須等待broker處理每個消息。
- 事務是重量級的,每次提交都需要fsync(),需要耗費大量的時間
- 事務非常耗性能,會降低RabbitMQ的消息吞吐量。
Confirm模式
這裏就引入了一種輕量級的方式一發送方確認(publisher confirm)機制。生產者將信道設置成confirm確認模式,一旦信道進入confirm模式,所有在該信道上面發佈的消息都會被指派一個唯一的ID( 從1開始),一旦消息被投遞到所有匹配的隊列之後,RabbitMQ就會發送一個確認(BasicAck) 給生產者(包含消息的唯一ID),這就使得生產者知曉消息已經正確到達了目的地了。如果消息和隊列是可持久化的,那麼確認消息會在消息寫入磁盤之後發出。
Comfirm模式的最大好處就是它是異步的。
開啓Confirm模式:
channel.ConfirmSelect();//開啓確認模式
編程模式:
- 普通,發一條,waitForConfirms()
- 批量的,發一批
- 異步confirm模式:提供一個回調方法