2.RabbitMQ进阶五中消息模型(Net8)
前言
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之后把消息发送到队列。
创作不易,感谢支持。

【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步