环境:
- win10
- rabbitmq-3.8.8
- .net core 3.1
- RabbitMQ.Client 6.2.1
- vs2019
安装RabbitMq环境参照:
说明:
这一部分的代码与上一篇:《消息队列3:rabbitmq入门实例》基本上没有出入。
这一片注重rabbitmq的模式和参数的说明。
一、rabbitmq模式概念
一般说rabbitmq有6种工作模式
- 简单模式;
- WorkQueue模式
- Pub/Sub(发布/订阅模式);
- Routing(路由模式);
- Topic(主题模式);
- Rpc模式;
注意:这种划分模式的方法不是很让人理解,因为太过表面,如果我们明白其中的原理就能明白这几种模式都是由
交换机
和队列
的不同组合演变来的。
.
不过这里先默认划分为上面的6种工作模式,并讲解WorkQueue模式
。
WorkQueue模式:
应用场景举例:
网站有一个在线pdf转word的功能,但是此功能需要耗时10s,在高并发的时候容易造成请求积压,造成系统卡死。此时可以使用
工作者模式
解决:
- web站点接收请求时将参数压入队列,返回一个轮训标记;
- 后端根据处理能力部署多个消费者去消费转换任务,转换完成后,将该任务的轮训标记和转换结果存入redis;
- 前端根据轮训标记每个3秒轮训一次。
直接用图形说明:
在这种模式下,可以有多个生产者同时向队列中发送消息,而接收方也可以有多个,但是一条消息只能发送给一个消费者,rabbitmq默认是轮训发送的,但是我们可以通过参数控制消费者接受消息的能力。
二、代码
准备解决方案如下:
发送端(send)和接收端(receive)都引用rabbitmq客户端:
<PackageReference Include="RabbitMQ.Client" Version="6.2.1" />
2.1 发送端的代码
using RabbitMQ.Client;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Send
{
class Program
{
static void Main(string[] args)
{
//1. 实例化连接工厂
var factory = new ConnectionFactory()
{
HostName = "localhost",//默认: localhost
Port = 5672,//默认: 5672
UserName = "guest",//默认: guest
Password = "guest",//默认: guest
VirtualHost = "/" //默认: /
};
//2. 建立连接
using (var connection = factory.CreateConnection())
{
var t = Task.Factory.StartNew(() =>
{
//3. 创建信道
var channel = connection.CreateModel();
{
//4. 声明队列 注意,同名的队列多次声明时以第一次声明的参数为准,其后声明的参数必须与第一次的声明一致,否则其后的声明会报错
channel.QueueDeclare(
queue: "hello",//队列名字
durable: true,//是否持久化消息(持久化后对于未消费的消息,rabbitmq可以保证重启后消息不丢失)
exclusive: false,//独占的,为true时表示: 当前队列只能同时被一个连接声明(一个连接的多个信道内可以多次声明),当connection关闭时队列被自动删除,队列内的数据不会保存,工作模式下这个参数设置为false,它的应用场景在pub/sub上,当sub加入时声明一个独占的队列,当sub断开时此队列就自动删除了
autoDelete: false,// 当这个队列的consumer从非0变为0的时候,这个队列会被删除,任何发向此队列的消息都会丢失,当有新的消费者出现时,消费者会接收到新发送的消息(注意: 只有在consumer从非0变为0的时候才会触发删除,刚开始没有消费者的时候,发向此队列的消息是会被积累的),工作模式下这个参数设置为false
arguments: null//其他参数信息
);
//5. 发送数据包
var index = 1;
while (true)
{
string message = $"chanel1-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} 测试消息:{index}";
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "", routingKey: "hello", basicProperties: null, body: body);
Console.WriteLine(" [x] Sent {0}", message);
Thread.Sleep(1000);
index++;
}
}
}, TaskCreationOptions.LongRunning);
t.Wait();
}
}
}
}
2.2 消费端的代码
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
using System.Threading;
namespace Receive
{
class Program
{
static void Main(string[] args)
{
//1.实例化连接工厂
var factory = new ConnectionFactory();
//2. 建立连接
using (var connection = factory.CreateConnection())
{
//3. 创建信道
using (var channel = connection.CreateModel())
{
//4. 声明队列
channel.QueueDeclare(queue: "hello", durable: false, exclusive: false, autoDelete: true, arguments: null);
//5. 构造消费者实例
var consumer = new EventingBasicConsumer(channel);
//6. 绑定消息接收后的事件委托
consumer.Received += (model, ea) =>
{
var message = Encoding.UTF8.GetString(ea.Body.ToArray());
Console.WriteLine(" [x] Received {0}", message);
Thread.Sleep(1000);//模拟耗时
Console.WriteLine(" [x] Done");
};
//7. 启动消费者
channel.BasicConsume(queue: "hello", autoAck: true, consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}
}
}
运行效果:
开启一个发送端和两个接收端(也可以开启两个发送端,这里为了演示接收端的消息竞争仅开一个发送端):
发送端的效果:
其中一个接受端效果:
最后,示例代码下载:
https://download.csdn.net/download/u010476739/16746253