RabbitMQ c#
一简介
1 MQ
(1) MQ简介
消息队列(Message Queue),是一种跨进程、异步的通信机制,用于上下游传递消息。由消息系统来确保消息的可靠传递
(2) 产生消息队列的原因
1> 不同进程(process)之间传递消息时,两个进程之间耦合程度过高,改动一个进程,引发必须修改另一个进程,为了隔离这两个进程,在两进程间抽离出一层(一个模块),所有两进程之间传递的消息,都必须通过消息队列来传递,单独修改某一个进程,不会影响另一个
2> 不同进程(process)之间传递消息时,为了实现标准化,将消息的格式规范化了,并且,某一个进程接受的消息太多,一下子无法处理完,并且也有先后顺序,必须对收到的消息进行排队,因此诞生了事实上的消息队列;
2 RabbitMQ
(1) RabbitMQ 简介
RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而群集和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。
(2) RabbitMQ服务支持的操作系统
linux, windows, macox
(3) RabbitMQ支持的开发语言
java, python. ruby, c#, c/c++, node.js, go 等
3 开发语言
Erlang
4 协议AMOP
AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有RabbitMQ等
二 RabbitMQ 模式介绍
1 简单模式
P:消息的生产者
C:消息的消费者
红色:队列
生产者将消息发送到队列,消费者从队列中获取消息
send code
static void mq_simple_mode_send_fun()
{
var host = "127.0.0.1";
var port = 5672;
var username = "readsense";
var password = "readface";
IConnection conn = null;
IModel channel = null;
try
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = host;
factory.Port = port;
factory.UserName = username;
factory.Password = password;
using (conn = factory.CreateConnection())
{
using (channel = conn.CreateModel())
{
string queueName = "SimpleQueue";
channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
string msg = "HelloWorld";
channel.BasicPublish("", queueName, null, Encoding.UTF8.GetBytes(msg));
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception error:" + ex.Message);
}
}
recv code
static void mq_simple_mode_recv_fun()
{
IConnection conn = null;
IModel channel = null;
try
{
var host = "127.0.0.1";
var port = 5672;
var username = "readsense";
var password = "readface";
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = host;
factory.Port = port;
factory.UserName = username;
factory.Password = password;
using (conn = factory.CreateConnection())
{
using (channel = conn.CreateModel())
{
string queueName = "SimpleQueue";
channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
var consumer = new QueueingBasicConsumer(channel);
// noAck: true 自动回复
// noAck: false 手动回复
// 现在BasicConsume函数 noAck 参数设置为手动回复
channel.BasicConsume(queueName, false, consumer);
while (true)
{
var ea = consumer.Queue.Dequeue();
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine("已接收:{0}", message);
// 回复
channel.BasicAck(ea.DeliveryTag, false);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception error:" + ex.Message);
}
}
2 work 模式
备注: work 模式代码 和 简单模式代码基本一致, 只需要增加公平分发的相关几行代码.具体如下:
特点
1 当有多个消费者时, 一个消息,只能有一个消费者获取获得
2 轮询分发 消费者1 和 消费者2 获取的消息数是相同的
3 公平分发 通过忙闲控制分发
注意点:
如果轮询分发, 打一个消费者比较忙时, 任然轮询是不合理的, 因此我们采用公平分发
1 我们使用basicQos( prefetchCount = 1)方法,来限制RabbitMQ只发不超过1条的消息给同一个消费者。当消息处理完毕后,有了反馈,才会进行第二次发送。
2 关闭自动应答,改为手动应答。
// 同一时刻服务器只会发一条消息给消费者
// 消费者代码增加 channel.basicQos(1);
// 现在BasicConsume函数 noAck 参数设置为手动回复
channel.BasicConsume(queueName, false, consumer);
// 手动确认回复
channel.BasicAck(ea.DeliveryTag, false);
3 订阅模式
exchange 类型: fanout
功能:一个生产者发送的消息会被多个消费者获取。一个生产者、一个交换机、多个队列、多个消费者
生产者:可以将消息发送到队列或者是交换机。
消费者:只能从队列中获取消息。
备注:
1 如果消息发送到没有队列绑定的交换机上,那么消息将丢失。
2 交换机不能存储消息,消息存储在队列中
3 消费者队列可以有多个消费者实例,只有其中一个消费者实例会消费
send code
// 订阅模式
static void mq_pubsub_mode_send_fun()
{
var host = "127.0.0.1";
var port = 5672;
var username = "readsense";
var password = "readface";
var exchange_name = "test_exchange_fanout";
IConnection conn = null;
IModel channel = null;
try
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = host;
factory.Port = port;
factory.UserName = username;
factory.Password = password;
using (conn = factory.CreateConnection())
{
using (channel = conn.CreateModel())
{
channel.ExchangeDeclare(exchange_name, "fanout");
string msg = "HelloWorld";
channel.BasicPublish(exchange_name, "", null, Encoding.UTF8.GetBytes(msg));
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception error:" + ex.Message);
}
}
recv code
// 订阅模式
static void mq_pubsub_mode_recv_fun()
{
IConnection conn = null;
IModel channel = null;
try
{
var host = "127.0.0.1";
var port = 5672;
var username = "readsense";
var password = "readface";
var exchange_name = "test_exchange_fanout";
var queueName = "test_queue_sub_1";
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = host;
factory.Port = port;
factory.UserName = username;
factory.Password = password;
using (conn = factory.CreateConnection())
{
using (channel = conn.CreateModel())
{
channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
channel.QueueBind(queueName, exchange_name, "");
channel.BasicQos(0, 1, false);
var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume(queueName, false, consumer);
while (true)
{
var ea = consumer.Queue.Dequeue();
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine("已接收:{0}", message);
channel.BasicAck(ea.DeliveryTag, false);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception error:" + ex.Message);
}
}
4 路由模式
exchange 类型: direct
生产者发送消息到交换机并且要指定路由key,消费者将队列绑定到交换机时需要指定路由key
send code
// 路由模式
static void mq_directexchange_mode_send_fun()
{
var host = "127.0.0.1";
var port = 5672;
var username = "readsense";
var password = "readface";
var exchange_name = "test_exchange_direct";
IConnection conn = null;
IModel channel = null;
try
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = host;
factory.Port = port;
factory.UserName = username;
factory.Password = password;
using (conn = factory.CreateConnection())
{
using (channel = conn.CreateModel())
{
channel.ExchangeDeclare(exchange_name, "direct");
for (int loop = 0; loop < 100; loop++)
{
Thread.Sleep(1000);
if (loop % 2 == 0)
{
string msg = "update info";
channel.BasicPublish(exchange_name, "update", null, Encoding.UTF8.GetBytes(msg));
}
else
{
string msg = "delete info";
channel.BasicPublish(exchange_name, "delete", null, Encoding.UTF8.GetBytes(msg));
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception error:" + ex.Message);
}
}
recv code
// 路由模式
static void Mqtt_DirectExchange_Mode_Recv_Fun()
{
IConnection conn = null;
IModel channel = null;
try
{
var host = "127.0.0.1";
var port = 5672;
var username = "readsense";
var password = "readface";
var exchange_name = "test_exchange_direct";
var queueName = "test_queue_direct_1";
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = host;
factory.Port = port;
factory.UserName = username;
factory.Password = password;
using (conn = factory.CreateConnection())
{
using (channel = conn.CreateModel())
{
channel.ExchangeDeclare(exchange_name, "direct");
channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
channel.QueueBind(queueName, exchange_name, "delete");
// channel.QueueBind(queueName, exchange_name, "delete");
channel.BasicQos(0, 1, false);
var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume(queueName, false, consumer);
while (true)
{
var ea = consumer.Queue.Dequeue();
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine("已接收:{0}", message);
channel.BasicAck(ea.DeliveryTag, false);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception error:" + ex.Message);
}
}
5 主题模式(通配符模式)
Topic Exchange - 将路由键和某模式进行匹配, 此时队列需要绑定某个模式上, 符号"#“匹配一个或多个字符,”*"匹配一个字符.
send code
// topic模式
static void mq_topic_mode_send_fun()
{
var host = "127.0.0.1";
var port = 5672;
var username = "readsense";
var password = "readface";
var exchange_name = "test_exchange_topic";
IConnection conn = null;
IModel channel = null;
try
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = host;
factory.Port = port;
factory.UserName = username;
factory.Password = password;
using (conn = factory.CreateConnection())
{
using (channel = conn.CreateModel())
{
channel.ExchangeDeclare(exchange_name, "topic");
for (int loop = 0; loop < 100; loop++)
{
Thread.Sleep(1000);
if (loop % 2 == 0)
{
string msg = "update info";
channel.BasicPublish(exchange_name, "routekey.1", null, Encoding.UTF8.GetBytes(msg));
}
else
{
string msg = "delete info";
channel.BasicPublish(exchange_name, "routekey.2", null, Encoding.UTF8.GetBytes(msg));
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception error:" + ex.Message);
}
}
recv code
// topic 模式:
static void mq_topic_mode_recv_fun()
{
IConnection conn = null;
IModel channel = null;
try
{
var host = "127.0.0.1";
var port = 5672;
var username = "readsense";
var password = "readface";
var exchange_name = "test_exchange_topic";
var queueName = "test_queue_topic_1";
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = host;
factory.Port = port;
factory.UserName = username;
factory.Password = password;
using (conn = factory.CreateConnection())
{
using (channel = conn.CreateModel())
{
channel.ExchangeDeclare(exchange_name, "topic");
channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null);
channel.QueueBind(queueName, exchange_name, "routekey.#");
// channel.QueueBind(queueName, exchange_name, "delete");
channel.BasicQos(0, 1, false);
var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume(queueName, false, consumer);
while (true)
{
var ea = consumer.Queue.Dequeue();
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine("已接收:{0}", message);
channel.BasicAck(ea.DeliveryTag, false);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception error:" + ex.Message);
}
}
三 参考文档
1 RabbitMQ参考文档
https://blog.csdn.net/hellozpc/article/details/81436980
备注: rabbitmq 安装可参考上面文档
2 RabbitMQ官网文档
https://www.rabbitmq.com/getstarted.html