本文章僅爲個人理解,如有錯誤請指正。
RabbitMQ安裝、說明、常用命令:https://blog.csdn.net/kang_xuan/article/details/90718878
RabbitMQ API:
Nuget UI:查找 RabbitMQ.Client
Nuget CLI: Install-Package RabbitMQ.Client -Version 5.1.0 (查找對應版本)
RabbitMQ.Client、RabbitMQ.ServiceMode兩個程序集都需要進行引用。
RabbitMQ五種隊列介紹:
- RabbitMQ相關設置
- 安裝RabbitMQ
- 創建虛擬機:rabbitmqctl add_vhost /demo
- 創建用戶:rabbitmqctl add_user kk2019jy kk2019jy
- 設置用戶權限:rabbitmqctl set_permissions -p /demo kk2019jy .* .* .*
- 注意事項:
- 建議所有隊列模式先創建隊列後在進行生產者發送數據操作。
- 建議消費者不使用using調用Dispose,手動說着註冊在其他事件中進行調用對象Dispose操作,Demo中使用WinForm方式,所以FormClosing進行了資源的釋放。
- 自動消息確認和手動消息確認,同時只能使用一種方式,如果使用手動確認消息,建議在消費者事件的最後一行代碼進行確認操作。
- 簡單隊列
只有一個生產者,一個消費者,生產者發送消息到交換機,消費者接收隊列消息並進行確認。只需要創建隊列即可,不需要對交換機和隊列進行綁定設置,默認使用AMQP default交換機,會將隊列的名稱作爲路由鍵與交換機進行綁定。
//創建隊列
private void button1_Click(object sender, EventArgs e)
{
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
//創建連接
using (IConnection connectionObj = connectionFactory.CreateConnection())
{
//創建管道
using (IModel modelChannel = connectionObj.CreateModel())
{
//聲明隊列,設置隊列持久化
modelChannel.QueueDeclare("hello", true, false, false, null);
}
}
}
//生產者
private void button2_Click(object sender, EventArgs e)
{
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
using (IConnection connectionObj = connectionFactory.CreateConnection())
{
//創建管道
using (IModel modelChannel = connectionObj.CreateModel())
{
byte[] buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new MessageInfo { StrValue = "123", IntValue = Num++ }));
//發送消息,設置路由鍵爲hello
modelChannel.BasicPublish("", "hello", null, buffer);
}
}
}
//消費者
private void button3_Click(object sender, EventArgs e)
{
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
ConnectionObj = connectionFactory.CreateConnection();
//創建管道
ModelChannel = ConnectionObj.CreateModel();
//創建基本消費者事件對象
EventingBasicConsumer consumerEvent = new EventingBasicConsumer(ModelChannel);
//註冊事件
consumerEvent.Received += ConsumerEvent_Received;
//消費者接收消息,關閉自動消息確認
ModelChannel.BasicConsume("hello", false, consumerEvent);
}
private void ConsumerEvent_Received(object sender, BasicDeliverEventArgs e)
{
string byteStr = Encoding.UTF8.GetString(e.Body);
JsonConvert.DeserializeObject(byteStr);
Console.WriteLine(byteStr);
//手動進行消息確認
(sender as EventingBasicConsumer).Model.BasicAck(e.DeliveryTag, false);
}
- Work隊列模式
也稱工作隊列模式,競爭消費者模式,一個生產者,多個消費者,一個消息只能被一個消費者接收處理,Work模式有兩種設置:循環調度和公平派遣,應用場景:效率高的消費者消費消息多。可以用來進行負載均衡
//循環調度-創建隊列
private void button5_Click(object sender, EventArgs e)
{
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
//創建連接
using (IConnection connectionObj = connectionFactory.CreateConnection())
{
//創建管道
using (IModel modelChannel = connectionObj.CreateModel())
{
//聲明隊列,非持久保持,不自動刪除隊列
modelChannel.QueueDeclare("roundQueue", true, false, false, null);
}
}
}
//循環調度-生產者發送消息
private void button6_Click(object sender, EventArgs e)
{
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
using (IConnection connectionObj = connectionFactory.CreateConnection())
{
//創建管道
using (IModel modelChannel = connectionObj.CreateModel())
{
for (int i = 0; i < 20; i++)
{
//創建屬性
IBasicProperties basicProperties = modelChannel.CreateBasicProperties();
//設置消息爲持久性
basicProperties.Persistent = true;
byte[] buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new MessageInfo { StrValue = "123", IntValue = Num++ }));
//基礎發佈消息
modelChannel.BasicPublish("", "roundQueue", basicProperties, buffer);
}
}
}
}
//循環調度-消費者接收數據
private void button7_Click(object sender, EventArgs e)
{
//線進行消費者監聽操作
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
IConnection connectionObj = connectionFactory.CreateConnection();
//創建管道
IModel modelChannel = connectionObj.CreateModel();
//創建基本消費者事件對象
EventingBasicConsumer consumerEvent = new EventingBasicConsumer(modelChannel);
//註冊事件
consumerEvent.Received += WorkConsumerEvent_Received;
//基礎消費者,接收輸入,關閉自動消息確認
modelChannel.BasicConsume("roundQueue", false, consumerEvent);
IModel ModelChannelSleep = connectionObj.CreateModel();
EventingBasicConsumer consumerEventSleep = new EventingBasicConsumer(ModelChannelSleep);
consumerEventSleep.Received += WorkConsumerEventSleep_Received;
ModelChannelSleep.BasicConsume("roundQueue", false, consumerEventSleep);
}
void WorkConsumerEvent_Received(object sender, BasicDeliverEventArgs e)
{
string byteStr = Encoding.UTF8.GetString(e.Body);
MessageInfo info = JsonConvert.DeserializeObject<MessageInfo>(byteStr);
Console.WriteLine("Consume-1: " + info.IntValue + " " + e.RoutingKey);
//模擬消費者處理消息耗時,實現循環調度和公平派遣的兩個消費者接收消息情況
Thread.Sleep(1000);
//手動進行消息確認
(sender as EventingBasicConsumer).Model.BasicAck(e.DeliveryTag, false);
}
void WorkConsumerEventSleep_Received(object sender, BasicDeliverEventArgs e)
{
string byteStr = Encoding.UTF8.GetString(e.Body);
MessageInfo info = JsonConvert.DeserializeObject<MessageInfo>(byteStr);
Console.WriteLine("Consume-2: " + info.IntValue + " " + e.RoutingKey);
//手動進行消息確認
(sender as EventingBasicConsumer).Model.BasicAck(e.DeliveryTag, false);
}
//公平派遣-創建隊列
private void button9_Click(object sender, EventArgs e)
{
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
//創建連接
using (IConnection connectionObj = connectionFactory.CreateConnection())
{
//創建管道
using (IModel modelChannel = connectionObj.CreateModel())
{
//聲明隊列
modelChannel.QueueDeclare("fairQueue", true, false, false, null);
}
}
}
//公平派遣-生產者發送消息
private void button10_Click(object sender, EventArgs e)
{
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
using (IConnection connectionObj = connectionFactory.CreateConnection())
{
//創建管道
using (IModel modelChannel = connectionObj.CreateModel())
{
for (int i = 0; i < 20; i++)
{
//創建屬性
IBasicProperties basicProperties = modelChannel.CreateBasicProperties();
//設置消息爲持久性
basicProperties.Persistent = true;
byte[] buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new MessageInfo { StrValue = "123", IntValue = Num++ }));
//發佈消息
modelChannel.BasicPublish("", "fairQueue", basicProperties, buffer);
}
}
}
}
//公平派遣-消費者接收數據
private void button8_Click(object sender, EventArgs e)
{
//線進行消費者監聽操作
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
IConnection connectionObj = connectionFactory.CreateConnection();
//創建管道
IModel modelChannel = connectionObj.CreateModel();
//設置在尚未進行消息確認之前不能向消費者發送消息
modelChannel.BasicQos(0, 1, false);
EventingBasicConsumer consumerEvent = new EventingBasicConsumer(modelChannel);
consumerEvent.Received += WorkConsumerEvent_Received;
modelChannel.BasicConsume("fairQueue", false, consumerEvent);
IModel ModelChannelSleep = connectionObj.CreateModel();
//設置在尚未進行消息確認之前不能向消費者發送消息
ModelChannelSleep.BasicQos(0, 1, false);
EventingBasicConsumer consumerEventSleep = new EventingBasicConsumer(ModelChannelSleep);
consumerEventSleep.Received += WorkConsumerEventSleep_Received;
ModelChannelSleep.BasicConsume("fairQueue", false, consumerEventSleep);
}
- 發佈/訂閱模式
需使用扇出模式交換機,一個生產者,多個消費者,一次向多個消費者發送消息。應用場景舉例:一個商城系統需要在管理員上傳商品新的圖片時,前臺系統必須更新圖片,日誌系統必須記錄相應的日誌,那麼就可以將兩個隊列綁定到圖片上傳交換器上,一個用於前臺系統更新圖片,另一個用於日誌系統記錄日誌。
臨時隊列:是由RabbitMQ隨機生成隊列名而產生的隊列,生產者和消費者之間共享隊列時,爲隊列命名非常重要,發佈/訂閱模式,消費者和生產者之間無需共享隊列,一個隊列是無法實現發送一次消息多個消費者接收。生成格式類似於mq.gen-JzTY20BRgKO-HjmUJj0wLg。
private void button12_Click(object sender, EventArgs e)
{
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
using (IConnection connectionObj = connectionFactory.CreateConnection())
{
//創建管道
using (IModel modelChannel = connectionObj.CreateModel())
{
//聲明交換,如果交換不存在則創建,採用扇出模式,實現發送到交換機上的消息會被推送到綁定在交換機上的所有隊列
modelChannel.ExchangeDeclare("PubSubExchange", "fanout");
for (int i = 0; i < 20; i++)
{
//創建基礎屬性
IBasicProperties basicProperties = modelChannel.CreateBasicProperties();
//設置消息爲持久性
basicProperties.Persistent = true;
byte[] buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new MessageInfo { StrValue = "123", IntValue = Num++ }));
//發佈消息到指定的交換機
modelChannel.BasicPublish("PubSubExchange", "", basicProperties, buffer);
}
}
}
}
private void button13_Click(object sender, EventArgs e)
{
//線進行消費者監聽操作
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
IConnection connectionObj = connectionFactory.CreateConnection();
//創建管道
IModel modelChannel = connectionObj.CreateModel();
//聲明交換,如果交換不存在則創建
modelChannel.ExchangeDeclare("PubSubExchange", "fanout");
//創建臨時隊列
string name = modelChannel.QueueDeclare().QueueName;
modelChannel.QueueBind(name, "PubSubExchange", "");
//創建基本消費者事件對象
EventingBasicConsumer consumerEvent = new EventingBasicConsumer(modelChannel);
//註冊事件代碼請看Work隊列模式中WorkConsumerEvent_Received
consumerEvent.Received += WorkConsumerEvent_Received;
//基礎消費者,接收輸入,設置非自動
modelChannel.BasicConsume(name, false, consumerEvent);
IModel ModelChannelSleep = connectionObj.CreateModel();
ModelChannelSleep.ExchangeDeclare("PubSubExchange", "fanout");
string sleepName=ModelChannelSleep.QueueDeclare().QueueName;
ModelChannelSleep.QueueBind(sleepName, "PubSubExchange", "");
EventingBasicConsumer consumerEventSleep = new EventingBasicConsumer(ModelChannelSleep);
//註冊事件代碼請看Work隊列模式中WorkConsumerEventSleep_Received
consumerEventSleep.Received += WorkConsumerEventSleep_Received;
ModelChannelSleep.BasicConsume(sleepName, false, consumerEventSleep);
}
路由模式
需要使用直接模式交換機,類似於發佈/訂閱模式,生產者發送消息到交換機需要指定路由鍵,隊列和交換機綁定也需要指定路由鍵,當交換機推送消息到隊列時,會根據消息的路由鍵對比隊列綁定時所使用的路由鍵是否一致,如果一致則進行轉發。交換機和一個隊列可以進行多次綁定,實現綁定多個路由鍵。
private void button15_Click(object sender, EventArgs e)
{
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
using (IConnection connectionObj = connectionFactory.CreateConnection())
{
//創建管道
using (IModel modelChannel = connectionObj.CreateModel())
{
//聲明隊列,非持久保持,不自動刪除隊列
modelChannel.ExchangeDeclare("RouteExchange", "direct");
for (int i = 0; i < 20; i++)
{
//創建基礎屬性
IBasicProperties basicProperties = modelChannel.CreateBasicProperties();
//設置消息爲持久性
basicProperties.Persistent = true;
byte[] buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new MessageInfo { StrValue = "123", IntValue = Num++ }));
if (i % 3 == 0)
//基礎發佈消息
modelChannel.BasicPublish("RouteExchange", "Three", basicProperties, buffer);
else if (i % 3 == 1)
modelChannel.BasicPublish("RouteExchange", "Two", basicProperties, buffer);
else
modelChannel.BasicPublish("RouteExchange", "One", basicProperties, buffer);
}
}
}
}
private void button14_Click(object sender, EventArgs e)
{
//線進行消費者監聽操作
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
IConnection connectionObj = connectionFactory.CreateConnection();
//創建管道
IModel modelChannel = connectionObj.CreateModel();
modelChannel.ExchangeDeclare("RouteExchange", "direct");
string name = modelChannel.QueueDeclare().QueueName;
modelChannel.QueueBind(name, "RouteExchange", "Three");
//創建基本消費者事件對象
EventingBasicConsumer consumerEvent = new EventingBasicConsumer(modelChannel);
//註冊事件
consumerEvent.Received += WorkConsumerEvent_Received;
//基礎消費者,接收輸入,設置非自動
modelChannel.BasicConsume(name, false, consumerEvent);
IModel ModelChannelSleep = connectionObj.CreateModel();
ModelChannelSleep.ExchangeDeclare("RouteExchange", "direct");
string sleepName = ModelChannelSleep.QueueDeclare().QueueName;
ModelChannelSleep.QueueBind(sleepName, "RouteExchange", "Two");
ModelChannelSleep.QueueBind(sleepName, "RouteExchange", "One");
EventingBasicConsumer consumerEventSleep = new EventingBasicConsumer(ModelChannelSleep);
consumerEventSleep.Received += WorkConsumerEventSleep_Received;
ModelChannelSleep.BasicConsume(sleepName, false, consumerEventSleep);
}
主題模式
又稱通配符模式,需使用主題模式交換機,路由模式雖然存在可以根據路由鍵進行推送消息到隊列,但是比較具有侷限性,不夠靈活。主題模式交換機不能接受任意的路由鍵消息,它必須是由點分隔的單詞列表,單詞可以是任何內容。主題模式交換機將特定路由密鑰發送的消息將被傳遞到與匹配的綁定密鑰綁定的所有隊列。
主題模式交換機匹配規則:使用*(星號)可以代替一個單詞,使用#(Hash)可以替代一個或多個單詞。
例如:Q1隊列與主題交換機綁定使用路由鍵*.orage.#,Q2隊列與主題交換機綁定使用路由鍵*.rabbit。生產者發送消息的路由鍵當爲any.orage會推送到Q1隊列,當路由鍵爲my.rabbit會推送到Q2隊列
private void button18_Click(object sender, EventArgs e)
{
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
using (IConnection connectionObj = connectionFactory.CreateConnection())
{
//創建管道
using (IModel modelChannel = connectionObj.CreateModel())
{
//聲明交換機,且設置爲主題模式
modelChannel.ExchangeDeclare("TopicsExchange", "topic");
for (int i = 0; i < 20; i++)
{
//創建基礎屬性
IBasicProperties basicProperties = modelChannel.CreateBasicProperties();
//設置消息爲持久性
basicProperties.Persistent = true;
byte[] buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new MessageInfo { StrValue = "123", IntValue = Num++ }));
if (i % 3 == 0)
//基礎發佈消息
modelChannel.BasicPublish("TopicsExchange", i + ".Three", basicProperties, buffer);
else if (i % 3 == 1)
modelChannel.BasicPublish("TopicsExchange", i + ".Other", basicProperties, buffer);
else
modelChannel.BasicPublish("TopicsExchange", i + ".Three.hhh" + i, basicProperties, buffer);
}
}
}
}
private void button17_Click(object sender, EventArgs e)
{
//線進行消費者監聽操作
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "192.168.116.2";
connectionFactory.VirtualHost = "/demo";
connectionFactory.UserName = "kk2019jy";
connectionFactory.Password = "kk2019jy";
IConnection connectionObj = connectionFactory.CreateConnection();
//創建管道
IModel modelChannel = connectionObj.CreateModel();
modelChannel.ExchangeDeclare("TopicsExchange", "topic");
string name = modelChannel.QueueDeclare().QueueName;
modelChannel.QueueBind(name, "TopicsExchange", "#.Three.#");
//創建基本消費者事件對象
EventingBasicConsumer consumerEvent = new EventingBasicConsumer(modelChannel);
//註冊事件
consumerEvent.Received += WorkConsumerEvent_Received;
//基礎消費者,接收輸入,設置非自動
modelChannel.BasicConsume(name, false, consumerEvent);
IModel ModelChannelSleep = connectionObj.CreateModel();
ModelChannelSleep.ExchangeDeclare("TopicsExchange", "topic");
string sleepName = ModelChannelSleep.QueueDeclare().QueueName;
ModelChannelSleep.QueueBind(sleepName, "TopicsExchange", "*.Other");
EventingBasicConsumer consumerEventSleep = new EventingBasicConsumer(ModelChannelSleep);
consumerEventSleep.Received += WorkConsumerEventSleep_Received;
ModelChannelSleep.BasicConsume(sleepName, false, consumerEventSleep);
}