前言
RabbitMQ進階。
官方地址:https://www.rabbitmq.com/tutorials/tutorial-one-dotnet
1.Hello World
點對點模式。
1.1傳送
傳送10條訊息
internal class SendHelloWorld
{
public static void SendSendHelloWorldMsg(ConnectionFactory connectionFactory)
{
using var connection = connectionFactory.CreateConnection();
using var channel = connection.CreateModel();
string qName = "helloqueue";
//建立佇列
channel.QueueDeclare(queue: qName,
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
int i = 0;
while (i < 10)
{
string content = $"[Send] hello,這是第{i}條";
byte[] body = Encoding.UTF8.GetBytes(content);
// 傳送訊息
channel.BasicPublish(exchange: "", routingKey: qName, null, body);
Console.WriteLine($"傳送第{i}條完畢");
i++;
}
}
}
執行:
1.2接收
接收訊息
internal class ReceiveHelloWorld
{
public static void ReceiveHelloWorldMsg(ConnectionFactory connectionFactory) {
using var connection = connectionFactory.CreateConnection();
using var channel = connection.CreateModel();
string qName = "helloqueue";
//建立佇列 如果先啟動消費者,且又不建立佇列,程式異常
channel.QueueDeclare(qName, false, false, false, null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($"[Received] {message}");
};
channel.BasicConsume(queue: qName,
autoAck: true,
consumer: consumer);
}
}
執行:
此時佇列訊息已被消費完畢。
1.3預設交換機
如果傳送端並沒有指定交換機
沒有交換機走預設的交換機
2.簽收和拒絕
訊息沒有被消費。
自動簽收,佇列被消費掉了。
拒絕簽收後訊息回到原來的佇列,會一直接收和返回。
訊息拒絕後不返回佇列,訊息就沒了。
channel.BasicReject(deliveryTag: ea.DeliveryTag, requeue: false);
3.Work Queues
工作佇列模式。
模擬倆個客戶端,手動簽收,並且第二個客戶端修休眠時間200ms,模擬處理訊息慢。
先啟動來個客戶端,等待接收訊息。
新建100條訊息,倆個客戶端開始接收訊息,明顯下面那個處理的更快,接收的更多,因為休眠時間短。
4.Publish/Subscribe
釋出訂閱模式(廣播、扇形)。
建立交換機pubsubexchange,並繫結三個佇列,向交換機pubsubexchange傳送10條訊息,pubsubexchange繫結的每個佇列都收到了訊息。
客戶端指定queueName2消費了10條訊息。
5.Routing
路由模型。
建立direct模式的交換機routingexchange,然後指定key1傳送訊息。
只消費routingqueue1的訊息。
6.Topics
主題模式。
路由模式和主題模式主要是交換機型別和匹配規則不一樣。
建立direct模式的交換機topicsexchange,然後指定key.*和key.#傳送訊息。
key.*後面匹配一個單詞。
key.#後面匹配一個或多個單詞。
所以topicsqueue1和topicsqueue1複合匹配規則,接收到了訊息。
這裡只處理佇列topicsqueue1的訊息,topicsqueue1的訊息被消費了。
7.訊息持久化和非持久化
重啟伺服器後之前建立的交換機和佇列都沒有了。
7.1持久化
-
記憶體到磁碟。保證:訊息確認簽收之前,不能丟。
-
要做到訊息持久化,必須:交換機、佇列、訊息都要持久化。
-
持久化的交換機可以繫結沒有持久化的佇列;持久化的佇列裡可以放非持久化訊息。
交換機持久化
using var connection = connectionFactory.CreateConnection();
using var channel = connection.CreateModel();
// 宣告交換機物件
string exchangeName = "topicsexchange";//交換機名稱
channel.ExchangeDeclare(exchangeName, "topic", durable: true);//扇形輸出 durable持久化
佇列持久化
// 建立佇列 持久化
string queueName1 = "topicsqueue1";
channel.QueueDeclare(queueName1, durable: true, false, false, null);
訊息持久化
// 通道建立屬性
var prop = channel.CreateBasicProperties();
// 訊息持久化
prop.Persistent = true;
持久化:
建立的交換機和佇列都變成了持久化。
D:代表持久化。
7.2非持久化
能持久化,但是一般不做。記憶體快耗盡的時候進行持久化。
未持久化。
8.死信佇列
RabbitMQ 裡,當訊息在佇列中變成死信(消費者無法正常處理的訊息
)之後,它會被重新投遞到一個交換機上(即死信交換機),死信交換機上繫結的消費佇列就是死信佇列。
死信產生需要滿足如下條件:
- 訊息被消費者手動拒絕接收,並且
requeue
(重新加入佇列)策略為 False; - 訊息已經過期(TTL);
- 佇列達到最大長度,訊息裝不下了。
8.1超時
模擬一下超時後的死信佇列。
將正常佇列繫結死信交換機。
Dictionary<string, object> para = new Dictionary<string, object>();
para.Add("x-dead-letter-exchange", deadExName);//死信交換機名稱
para.Add("x-dead-letter-routing-key", deadKey);//死信路由名稱
//para.Add("x-max-length",10);//最多10條
channel.QueueDeclare(normalQueName, false, false, false, para);
設定訊息10s超時,然後自動回到私信佇列。
//傳送訊息(向正常佇列傳送)
var prop = channel.CreateBasicProperties();
prop.Expiration = "10000";//設定有效期 毫秒
channel.BasicPublish(exchange: normalExName, routingKey: normalKey, prop, body);
10s後normalqueue的資訊未消費,未消費的訊息重新繫結到deadqueue。
8.2正常情況
訊息在10s被正常消費嗎,就不會進入死信佇列。
8.3消費死信佇列
死信佇列訊息被消費完了。
8.4死信佇列大小限制
傳送20條訊息,normalqueue佇列最大儲存10條,還有10條進入到死信佇列。
Dictionary<string, object> para = new Dictionary<string, object>();
para.Add("x-dead-letter-exchange", deadExName);//死信交換機名稱
para.Add("x-dead-letter-routing-key", deadKey);//死信路由名稱
para.Add("x-max-length",10);//最多10條
channel.QueueDeclare(normalQueName, false, false, false, para);
8.5死信佇列消費端拒收
拒絕的10條訊息進入到死信佇列。
var consumer = new EventingBasicConsumer(channel);//消費者
consumer.Received += (m, e) =>
{
string msg = Encoding.UTF8.GetString(e.Body.ToArray());
// requeue: false 拒絕的訊息是否回到原佇列(normalqueue)
channel.BasicReject(e.DeliveryTag, requeue: false);
Console.WriteLine("已經消費了正常佇列訊息:" + msg);
};
channel.BasicConsume("normalqueue", false, consumer);
如果requeue=false拒絕的訊息回到佇列並且autoAck=false不自動接收,會出現死迴圈。
消費端一直接收一直拒絕。
channel.BasicReject(e.DeliveryTag, requeue: false);
9.延時佇列
9.1安裝延時佇列外掛
地址:https://www.rabbitmq.com/community-plugins
搜尋rabbitmq_delayed_message_exchange
下載
執行一下命令:
第一步:將外掛檔案複製到docker容器
docker cp
外掛地址\rabbitmq_delayed_message_exchange-3.13.0.ez
RabbitMQ容器id
:/opt/rabbitmq/plugins/rabbitmq_delayed_message_exchange-3.13.0.ez
# 例子:
docker cp C:\rabbitmq_delayed_message_exchange-3.13.0.ez 9dcba3e045fc:/opt/rabbitmq/plugins/rabbitmq_delayed_message_exchange-3.13.0.ez
可以透過命令檢視RabbitMQ容器id,也可以docker desktop檢視
第二步:進入docker容器
docker exec -it rabbitmq bash
第三步:啟用外掛
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
重新整理介面,Exchanges介面Type增加x-delayed-message。
9.2延時佇列
type: "x-delayed-message"表示延時型別交換機。
"x-delayed-type", "direct" 並且是direct模式。
// 定義一個延時型別的交換機
string exchangeName = "delayedexchange";
channel.ExchangeDeclare(
exchangeName,
type: "x-delayed-message",
durable: false,
autoDelete: false,
arguments: new Dictionary<string, object>
{
{ "x-delayed-type", "direct" }
}
);
"x-delay",10000表示訊息延遲10s傳送。
var body = Encoding.UTF8.GetBytes("這是我傳送的延時訊息");
var prop = channel.CreateBasicProperties();
prop.Headers = new Dictionary<string, object>() {
{ "x-delay",10000}//延時10秒
};
channel.BasicPublish(exchangeName, key, basicProperties: prop, body: body);
10s之後把訊息傳送到佇列。
創作不易,感謝支援。