关于安装rabbitmq这里一笔掠过了。

下面进入正题:

1.新建aspnetcorewebapi空项目,NormalQueue,删除controllers文件夹已经无关的文件,这里为了偷懒不用console控制台:

public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHostedService<ConsumerService>();
builder.Services.AddHostedService<DeadLetterExchangeConsuerService>();
builder.Services.AddHostedService<DelayExchangeConsumerService>();
var app = builder.Build(); // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.MapGet("/normal/{message}", ([FromRoute] string message) =>
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = "localhost";
factory.Port = 5672;
using (IConnection connection = factory.CreateConnection())
{
using (IModel channel = connection.CreateModel())
{
var queueName = "rbTest202301";
channel.QueueDeclare(queueName, true, false, false, null); {
string sendMessage = string.Format("Message_{0}", message);
byte[] buffer = Encoding.UTF8.GetBytes(sendMessage);
IBasicProperties basicProperties = channel.CreateBasicProperties();
basicProperties.DeliveryMode = 2; //持久化 1=非持久化
channel.BasicPublish("", queueName, basicProperties, buffer);
Console.WriteLine("消息发送成功:" + sendMessage);
}
}
}
}); app.MapGet("/deadletterexchange/{message}",([FromRoute] string message) =>{
DeadLetterExchange.Send(message);
}); app.MapGet("/delayexchange/{message}", ([FromRoute] string message) => {
DelayExchange.SendMessage(message);
});
app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
}
}

大概的介绍一下program文件:

这里有三个mini控制器,从这里发送对应的消息到rabbitmq

"/normal/{message}"   普通队列,
"/deadletterexchange/{message}" 死信队列
"/deadletterexchange/{message}"   延时队列

        builder.Services.AddHostedService<ConsumerService>();
builder.Services.AddHostedService<DeadLetterExchangeConsuerService>();
builder.Services.AddHostedService<DelayExchangeConsumerService>();

这里就是消费的服务,注册成HostedService。

ConsumerService代码如下:
 public class ConsumerService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine("normal Rabbitmq消费端开始工作!");
while (!stoppingToken.IsCancellationRequested)
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = "localhost";
factory.Port = 5672; IConnection connection = factory.CreateConnection();
{
IModel channel = connection.CreateModel();
{
var queueName = "rbTest202301";
channel.QueueDeclare(queueName, true, false, false, null);
//输入1,那如果接收一个消息,但是没有应答,则客户端不会收到下一个消息
channel.BasicQos(0, 1, false);
//在队列上定义一个消费者
var consumer = new EventingBasicConsumer(channel);
channel.BasicConsume(queueName, false, consumer);
consumer.Received += (ch, ea) =>
{
byte[] bytes = ea.Body.ToArray();
string str = Encoding.UTF8.GetString(bytes);
Console.WriteLine("队列消息:" + str.ToString());
//回复确认
channel.BasicAck(ea.DeliveryTag, false);
};
}
}
await Task.Delay(5000);
}
}
}

DeadLetterExchangeConsuerService代码如下:

 public class DeadLetterExchangeConsuerService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine("RabbitMQ消费端死信队列开始工作");
while (!stoppingToken.IsCancellationRequested)
{
DeadLetterExchange.Consumer();
await Task.Delay(5000);
}
}
}
  public class DeadLetterExchange
{
public static string dlxExchange = "dlx.exchange";
public static string dlxQueueName = "dlx.queue";
static string exchange = "direct-exchange";
static string queueName = "queue_Testdlx";
static string dlxExchangeKey = "x-dead-letter-exchange";
static string dlxQueueKey = "x-dead-letter-rounting-key";
public static void Send(string message)
{
using (var connection = new ConnectionFactory() { HostName = "localhost", Port = 5672 }.CreateConnection())
{
using(var channel = connection.CreateModel())
{ channel.ExchangeDeclare(exchange, ExchangeType.Direct, true, false); //创建交换机
channel.QueueDeclare(queueName, true, false, false,new Dictionary<string, object>
{
{ dlxExchangeKey,dlxExchange },
{dlxQueueKey,dlxQueueName }
}); // 创建队列
channel.QueueBind(queueName, exchange, queueName); var properties = channel.CreateBasicProperties();
properties.Persistent= true;//持久化
channel.BasicPublish(exchange,queueName,properties,Encoding.UTF8.GetBytes(message));
Console.WriteLine($"向队列:{queueName}发送消息:{message}");
}
}
} public static void Consumer()
{
var connection = new ConnectionFactory() { HostName = "localhost", Port = 5672 }.CreateConnection();
var channel = connection.CreateModel();
channel.ExchangeDeclare(dlxExchange, ExchangeType.Direct, true, false); //创建sixin交换机
channel.QueueDeclare(dlxQueueName, true, false, false); // 创建sixin队列
channel.QueueBind(dlxQueueName, dlxExchange, dlxQueueName); //绑定sixin队列sixin交换机 channel.ExchangeDeclare(exchange, ExchangeType.Direct, true, false); //创建交换机
channel.QueueDeclare(queueName, true, false, false, new Dictionary<string, object>
{
{ dlxExchangeKey,dlxExchange },
{dlxQueueKey,dlxQueueName }
}); // 创建队列
channel.QueueBind(queueName, exchange, queueName); var consumer = new EventingBasicConsumer(channel);
channel.BasicQos(0, 1, false);
consumer.Received += (model, ea) =>
{
var message = Encoding.UTF8.GetString(ea.Body.ToArray());
Console.WriteLine($"队列{queueName}消费消息:{message},不做ack确认");
channel.BasicNack(ea.DeliveryTag, false, requeue: false);
};
channel.BasicConsume(queueName, autoAck: false, consumer);
}
}

DelayExchangeConsumerService代码如下:

 public class DelayExchangeConsumerService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine("RabbitMQ消费端延迟队列开始工作");
while (!stoppingToken.IsCancellationRequested)
{ DelayExchange.Consumer();
await Task.Delay(5000);
}
}
}
 public class DelayExchange
{ public static void SendMessage(string message)
{
//死信交换机
string dlxexChange = "dlx.exchange";
//死信队列
string dlxQueueName = "dlx.queue"; //消息交换机
string exchange = "direct-exchange";
//消息队列
string queueName = "delay_queue"; using (var connection = new ConnectionFactory() { HostName = "localhost", Port = 5672 }.CreateConnection())
{
using (var channel = connection.CreateModel())
{
////创建死信交换机
//channel.ExchangeDeclare(dlxexChange, type: ExchangeType.Direct, durable: true, autoDelete: false);
////创建死信队列
//channel.QueueDeclare(dlxQueueName, durable: true, exclusive: false, autoDelete: false);
////死信队列绑定死信交换机
//channel.QueueBind(dlxQueueName, dlxexChange, routingKey: dlxQueueName); // 创建消息交换机
channel.ExchangeDeclare(exchange, type: ExchangeType.Direct, durable: true, autoDelete: false);
//创建消息队列,并指定死信队列,和设置这个队列的消息过期时间为10s
channel.QueueDeclare(queueName, durable: true, exclusive: false, autoDelete: false, arguments:
new Dictionary<string, object> {
{ "x-dead-letter-exchange",dlxexChange}, //设置当前队列的DLX(死信交换机)
{ "x-dead-letter-routing-key",dlxQueueName}, //设置DLX的路由key,DLX会根据该值去找到死信消息存放的队列
{ "x-message-ttl",10000} //设置队列的消息过期时间
});
//消息队列绑定消息交换机
channel.QueueBind(queueName, exchange, routingKey: queueName); var properties = channel.CreateBasicProperties();
properties.Persistent = true;
//properties.Expiration = "5000";发布消息,延时5s
//发布消息
channel.BasicPublish(exchange: exchange,
routingKey: queueName,
basicProperties: properties,
body: Encoding.UTF8.GetBytes(message));
Console.WriteLine($"{DateTime.Now},向队列:{queueName}发送消息:{message}");
}
}
} public static void Consumer()
{
//死信交换机
string dlxexChange = "dlx.exchange";
//死信队列
string dlxQueueName = "dlx.queue";
var connection = new ConnectionFactory() { HostName = "localhost", Port = 5672 }.CreateConnection();
{
//创建信道
var channel = connection.CreateModel();
{
//创建死信交换机
channel.ExchangeDeclare(dlxexChange, type: ExchangeType.Direct, durable: true, autoDelete: false);
//创建死信队列
channel.QueueDeclare(dlxQueueName, durable: true, exclusive: false, autoDelete: false);
//死信队列绑定死信交换机
channel.QueueBind(dlxQueueName, dlxexChange, routingKey: dlxQueueName); var consumer = new EventingBasicConsumer(channel);
channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: true);
consumer.Received += (model, ea) =>
{
//处理业务
var message = Encoding.UTF8.GetString(ea.Body.ToArray());
Console.WriteLine($"{DateTime.Now},队列{dlxQueueName}消费消息:{message}");
channel.BasicAck(ea.DeliveryTag, false);
};
channel.BasicConsume(dlxQueueName, autoAck: false, consumer);
}
}
}
}

延时队列实际应用场景可能比较复杂,比如每条消息的过期时间不一样,收到的消息的顺序有可能会乱掉。这些不做深究,自行百度。

关于死信队列常见应用场景之一下单,支付,支付超时的各种场景,下面通过一个简单的例子模拟一下

同样的新建一个空的webapi项目DeadLetterQueue,

program代码如下:

public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHostedService<ConsumerService>();
builder.Services.AddHostedService<DeadConsumerService>();
var app = builder.Build(); // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.MapGet("/normal/{message}", ([FromRoute] string message) =>
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = "localhost";
factory.Port = 5672;
using (IConnection connection = factory.CreateConnection())
{
using (IModel channel = connection.CreateModel())
{
var queueName = "rbTest2023010"; //channel.ExchangeDeclare("exchange.dlx", ExchangeType.Direct, true);
//channel.QueueDeclare("queue.dlx", true, false, false, null);
channel.ExchangeDeclare("exchange.normal", ExchangeType.Fanout, true);
channel.QueueDeclare(queueName, true, false, false,
new Dictionary<string, object>
{
{ "x-message-ttl" ,10000},
{"x-dead-letter-exchange","exchange.dlx" },
{"x-dead-letter-routing-key","routingkey" }
}
); channel.QueueBind(queueName, "exchange.normal", "");
{
string sendMessage = string.Format("Message_{0}", message);
byte[] buffer = Encoding.UTF8.GetBytes(sendMessage);
IBasicProperties basicProperties = channel.CreateBasicProperties();
basicProperties.DeliveryMode = 2; //持久化 1=非持久化
channel.BasicPublish("exchange.normal", queueName, basicProperties, buffer);
Console.WriteLine($"{DateTime.Now}消息发送成功:{sendMessage}" );
}
}
}
});
app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
}
}

下单后消费代码ConsumerService如下

 public class ConsumerService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine("normal Rabbitmq消费端开始工作!");
while (!stoppingToken.IsCancellationRequested)
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = "localhost";
factory.Port = 5672; IConnection connection = factory.CreateConnection();
{
IModel channel = connection.CreateModel();
{
var queueName = "rbTest2023010";
channel.ExchangeDeclare("exchange.normal", ExchangeType.Fanout, true);
channel.QueueDeclare(queueName, true, false, false, new Dictionary<string, object>
{
{ "x-message-ttl" ,10000},
{"x-dead-letter-exchange","exchange.dlx" },
{"x-dead-letter-routing-key","routingkey" }
}); channel.QueueBind(queueName, "exchange.normal", "");
//输入1,那如果接收一个消息,但是没有应答,则客户端不会收到下一个消息
channel.BasicQos(0, 1, false);
//在队列上定义一个消费者
var consumer = new EventingBasicConsumer(channel);
channel.BasicConsume(queueName, false, consumer);
consumer.Received += (ch, ea) =>
{
byte[] bytes = ea.Body.ToArray();
string str = Encoding.UTF8.GetString(bytes);
Console.WriteLine($"{DateTime.Now}来自死信队列获取的消息: {str.ToString()}");
//回复确认
if (str.Contains("跳过")) //假设超时不处理,留给后面deadconsumerservice处理
{
Console.WriteLine($"{DateTime.Now}来自死信队列获取的消息: {str.ToString()},该消息被拒绝");
channel.BasicNack(ea.DeliveryTag, false,false);
}
else //正常消息处理
{
Console.WriteLine($"{DateTime.Now}来自死信队列获取的消息: {str.ToString()},该消息被接受");
channel.BasicAck(ea.DeliveryTag, false);
}
}; }
}
await Task.Delay(5000);
}
}
}

通过模拟发送的消息加入跳过两个字会拒收这条消息,这样就会跳到设置的exchange.dlx交换机队列去,如果没有跳过那么这条消息就正常处理掉,消费确认。

超时不处理后我们通过新的消费服务DeadConsumerService来处理这异常的消费,比如回复库存,订单状态改为取消等等

public class DeadConsumerService:BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine("normal Rabbitmq消费端开始工作!");
while (!stoppingToken.IsCancellationRequested)
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = "localhost";
factory.Port = 5672; IConnection connection = factory.CreateConnection();
{
IModel channel = connection.CreateModel();
{
var queueName = "queue.dlx";
channel.ExchangeDeclare("exchange.dlx", ExchangeType.Direct, true);
channel.QueueDeclare("queue.dlx", true, false, false, null); channel.QueueDeclare(queueName, true, false, false, null); channel.QueueBind(queueName, "exchange.dlx", "");
//输入1,那如果接收一个消息,但是没有应答,则客户端不会收到下一个消息
channel.BasicQos(0, 1, false);
//在队列上定义一个消费者
var consumer = new EventingBasicConsumer(channel);
channel.BasicConsume("queue.dlx", false, consumer);
consumer.Received += (ch, ea) =>
{
byte[] bytes = ea.Body.ToArray();
string str = Encoding.UTF8.GetString(bytes);
Console.WriteLine($"{DateTime.Now}超时未处理的消息: {str.ToString()}");
//回复确认
{
channel.BasicAck(ea.DeliveryTag, false);
}
}; }
}
await Task.Delay(5000);
}
}
}

运行结果:

关于rabbitmq的死信队列和延时队列的介绍什么的这里不去贴baidu了,应用demo就这么多了,代码这里exercisebook/RabbitMQ.Test at main · liuzhixin405/exercisebook (github.com) 。小面分享一个完整一点的例子。

exercisebook/cat.seckill/cat.seckill at main · liuzhixin405/exercisebook (github.com)

感觉自己还是不合适写这些玩意儿,没有那么细心和耐心,有这时间真不如写写demo。

netcore下死RabbitMQ队列、死信队列、延时队列及小应用的更多相关文章

  1. 了解一下Redis队列【缓兵之计-延时队列】

    https://www.cnblogs.com/wt645631686/p/8454021.html 我们平时习惯于使用 Rabbitmq 和 Kafka 作为消息队列中间件,来给应用程序之间增加 异 ...

  2. 面试官:RabbitMQ过期时间设置、死信队列、延时队列怎么设计?

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 RabbitMQ我们经常的使用, ...

  3. rabbitmq死信队列和延时队列的使用

    死信队列&死信交换器:DLX 全称(Dead-Letter-Exchange),称之为死信交换器,当消息变成一个死信之后,如果这个消息所在的队列存在x-dead-letter-exchange ...

  4. RabbitMQ高级之消息限流与延时队列

    人生终将是场单人旅途,孤独之前是迷茫,孤独过后是成长. 楔子 本篇是消息队列RabbitMQ的第五弹. 上篇本来打算讲述RabbitMQ的一些高级用法: 如何保证消息的可靠性? 消息队列如何进行限流? ...

  5. 基于rabbitMQ 消息延时队列方案 模拟电商超时未支付订单处理场景

    前言 传统处理超时订单 采取定时任务轮训数据库订单,并且批量处理.其弊端也是显而易见的:对服务器.数据库性会有很大的要求,并且当处理大量订单起来会很力不从心,而且实时性也不是特别好 当然传统的手法还可 ...

  6. RabbitMQ:伪延时队列

    目录 一.什么是延时队列 二.RabbitMQ实现 三. 延时队列的问题 四.解决RabbitMQ的伪延时方案 ps:伪延时队列先卖个关子,我们先了解下延时队列. 一.什么是延时队列 所谓延时队列是指 ...

  7. RabbitMQ进阶使用-延时队列的配置(Spring Boot)

    依赖 MAVEN配置pom.xml <dependency> <groupId>org.springframework.boot</groupId> <art ...

  8. springboot项目整合rabbitMq涉及消息的发送确认,消息的消费确认机制,延时队列的实现

    1.引入maven依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactI ...

  9. RabbitMq 实现延时队列-Springboot版本

    rabbitmq本身没有实现延时队列,但是可以通过死信队列机制,自己实现延时队列: 原理:当队列中的消息超时成为死信后,会把消息死信重新发送到配置好的交换机中,然后分发到真实的消费队列: 步骤: 1. ...

  10. C# .net 环境下使用rabbitmq消息队列

    消息队列的地位越来越重要,几乎是面试的必问问题了,不会使用几种消息队列都显得尴尬,正好本文使用C#来带你认识rabbitmq消息队列 首先,我们要安装rabbitmq,当然,如果有现成的,也可以使用, ...

随机推荐

  1. Filter 筛选器(三)之 自定义一个启动事务的 TransactionScopeFilter

    如果一个方法内有多个写入操作,比如 写入A表,然后用A表的自增id 去写入B表,假如A表写入成功,但B表因为某种原因写入失败!(这就导致A表写入了脏数据) 这时候 我们可以自定义 一个Filter 进 ...

  2. 【MySQL】01_运算符、函数

    运算符 运算符是保留字或主要用于 SQL 语句的 WHERE 子句 中的字符,用于执行操作,例如:比较和算术运算. 这些运算符用于指定 SQL 语句中的条件,并用作语句中多个条件的连词. 常见运算符有 ...

  3. 云原生之旅 - 5)Kubernetes时代的包管理工具 Helm

    前言 上一篇文章 [基础设施即代码 使用 Terraform 创建 Kubernetes] 教会了你如何在Cloud上面建Kubernetes资源,那么本篇来讲一下如何在Kubernetes上面部署应 ...

  4. Python学习之实例2

    一.根据输入的各科成绩统计学生成绩是否达标 1 (背景:输入学生数量,以及各个学生语文.数学.英语.历史四科的成绩,如果总成绩小于 240,则成绩不达标,否则成绩合格) 2 3 #coding=utf ...

  5. created与mounted执行顺序(关于微任务与宏任务)

    1.ps:只要你只使用created或者mounted中的一个不就好了吗[dog].这样只要在第一个异步任务代码跳出前,嵌套第二个任务函数就好了 最后面的两个链接一个是微任务与宏任务的通俗例子,一个是 ...

  6. tools2

    [对身份证的校验] //身份证的校验 import java.util.stream.IntStream; /** * 身份证号码验证 * 1.号码的结构 * 公民身份号码是特征组合码,由十七位数字本 ...

  7. JUnit 5 单元测试教程

    点赞再看,动力无限. 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. 在软件开发过程中,我们通常都需要测试 ...

  8. VMware 虚拟机打开电源失败

    vmware上虚拟机关机导出ovf失败后,再次启动虚拟机,突然无法打开虚拟机,并伴随如下报错: 模块"disk"打开电源失败,无法打开磁盘/***/.../***.vmdk 解决方 ...

  9. 【云原生 · Kubernetes】kubernetes v1.23.3 二进制部署(三)

    5 部署 etcd 集群 etcd 是基于 Raft 的分布式 KV 存储系统,由 CoreOS 开发,常用于服务发现.共享配置以及并发控制(如 leader 选举.分布式锁等). kubernete ...

  10. 网络编程:软件开发架构、架构总结、网络编程前戏、OSI七层协议简介、OSI七层协议之物理连接层、数据链路层、网络相关专业名词、OSI七层协议之网络层

    目录 软件开发架构 架构总结 网络编程前戏 OSI七层协议简介 OSI七层协议之物理连接层 OSI七层协议之数据链路层 网络相关专业名词 OSI七层协议之网络层 OSI七层协议之传输层 软件开发架构 ...