使用示例:假设订单需要设置订单时效为15秒,超时后自动修改订单状态,使用rabbitmq进行实现。
实现前准备
1.环境搭建
参照https://blog.csdn.net/MoFe1/article/details/160134395?spm=1011.2415.3001.5331
2.安装延时插件
插件名称:rabbitmq_delayed_message_exchange
原理讲解
-
RabbitMQ 本身并没有直接提供延迟消息的功能,但可以借助
rabbitmq_delayed_message_exchange插件来实现。这个插件允许消息在经过指定的延迟时间后才被投递到队列中。 -
基本流程如下:
-
订单创建时,发送一条带有 15秒钟 延迟属性的消息到 RabbitMQ。
-
RabbitMQ 延迟插件根据延迟时间将消息暂存,15秒钟 后将消息发送到对应的队列。
-
消费者从队列中获取消息,判断订单是否超时(因为有可能在这 15秒钟内订单状态已经被正常修改,所以还需要再次确认),如果超时则修改订单状态。
-
-
安装插件 :
- 确认前置条件 :确保已正确安装 RabbitMQ 及其依赖的 Erlang 环境,且 RabbitMQ 的 Management 插件已启用。可通过命令
rabbitmq - plugins list查看已安装插件,若未启用 Management 插件,可进入 RabbitMQ 安装目录下的sbin目录(默认路径为C:\Program Files\RabbitMQ Server\rabbitmq_server - x.x.x\sbin),执行rabbitmq - plugins enable rabbitmq_management命令启用。 - 下载插件 :前往 [RabbitMQ 官方社区插件页面](https://www.rabbitmq.com/community - plugins),找到 Routing 分类下的
rabbitmq_delayed_message_exchange插件,点击Releases链接跳转到 GitHub 发布页,下载与 RabbitMQ 版本匹配的.ez格式插件文件。 - 放置插件文件 :将下载好的
.ez文件复制到 RabbitMQ 的插件目录,默认路径为%APPDATA%\RabbitMQ\plugins,若该目录不存在可自行创建。也可复制到C:\Program Files\RabbitMQ Server\rabbitmq_server - x.x.x\plugins目录下。 - 启用插件 :切换到 RabbitMQ 安装目录下的
sbin目录,在命令行中执行rabbitmq - plugins enable rabbitmq_delayed_message_exchange命令。若提示 "The plugin has been successfully enabled.",则表示插件启用成功。 - 验证插件状态 :执行
rabbitmq - plugins list | findstr delayed命令,若返回结果显示(E*) rabbitmq_delayed_message_exchange...,则表明插件已正常加载。也可登录 RabbitMQ 管理界面(默认地址为http://localhost:15672/),查看 Exchange 类型选项中是否新增了x - delayed - message条目。
验证插件是否生效 :
-
可在服务页面中添加交换机界面查看是否出现标记内容

-
或者
-
打开命令行 :在当前文件夹空白处按住
Shift键 + 右键,选择 "在此处打开命令窗口" 或 "打开 PowerShell 窗口"。 -
启用插件 :在命令行中输入:
bash
rabbitmq-plugins.bat enable rabbitmq_delayed_message_exchange -
验证状态 :执行以下命令查看插件是否启用成功:
bash
rabbitmq-plugins.bat list | findstr delayed如果输出包含
(E*) rabbitmq_delayed_message_exchange,说明插件已生效。
bashMicrosoft Windows [版本 10.0.19045.6466] (c) Microsoft Corporation。保留所有权利。 C:\Users\Administrator>rabbitmq-plugins.bat list | findstr delayed [E*] rabbitmq_delayed_message_exchange 3.12.0 C:\Users\Administrator> - 确认前置条件 :确保已正确安装 RabbitMQ 及其依赖的 Erlang 环境,且 RabbitMQ 的 Management 插件已启用。可通过命令
3.代码及测试
订单实体类,在订单实体类上添加RabbitMq特性用于设置实体属性
cs
/// <summary>
/// 创 建:超级管理员
/// 日 期:2026-03-27 10:59
/// 描 述:业务订单表实体类
/// </summary>
[RabbitMq(
Queue = "OrderTimeLimit",
///Exchange = "OrderTimeLimitExchange",
///ExchangeType = "direct",
RoutingKey = "OrderTimeLimit",
MaximumPriority = 10,
RetryCount = 3,
LazyMode = "lazy",
DeadLetter = true,
DeadLetterExchange = "OrderTimeLimit.dle",
DeadLetterRoutingKey = "OrderTimeLimit.dle.key"
)]
[SugarTable("data_businessorder")]
public class BusinessorderEntity : IEntity<BusinessorderEntity>,ICreationAudited,IModificationAudited,IDeleteAudited
{
/// <summary>
/// 主键Id
/// </summary>
[SugarColumn(ColumnName="F_Id", ColumnDescription = "主键Id",ColumnDataType = "varchar(50)",IsPrimaryKey = true)]
public string F_Id { get; set; }
/// <summary>
/// 信息表ID
/// </summary>
[SugarColumn(ColumnName = "F_BaseId", ColumnDescription = "信息表ID", ColumnDataType = "varchar(50)", IsNullable = true)]
public string F_BaseId { get; set; }
/// <summary>
/// 删除标记
/// </summary>
[SugarColumn(ColumnName="F_DeleteMark", ColumnDescription = "删除标记",ColumnDataType = "tinyint(1)", IsNullable = true)]
public bool? F_DeleteMark { get; set; }
/// <summary>
/// 有效标记
/// </summary>
[SugarColumn(ColumnName="F_EnabledMark", ColumnDescription = "有效标记",ColumnDataType = "tinyint(1)", IsNullable = true)]
public bool? F_EnabledMark { get; set; }
/// <summary>
/// 订单创建时间
/// </summary>
[SugarColumn(ColumnName="F_CreatorTime", ColumnDescription = "订单创建时间",ColumnDataType = "datetime", IsNullable = true)]
public DateTime? F_CreatorTime { get; set; }
/// <summary>
/// 订单创建人用户ID
/// </summary>
[SugarColumn(ColumnName="F_CreatorUserId", ColumnDescription = "订单创建人用户ID",ColumnDataType = "varchar(50)", IsNullable = true)]
public string F_CreatorUserId { get; set; }
/// <summary>
/// 修改时间
/// </summary>
[SugarColumn(ColumnName="F_LastModifyTime", ColumnDescription = "修改时间",ColumnDataType = "datetime", IsNullable = true)]
public DateTime? F_LastModifyTime { get; set; }
/// <summary>
/// 修改人Id
/// </summary>
[SugarColumn(ColumnName="F_LastModifyUserId", ColumnDescription = "修改人Id",ColumnDataType = "varchar(50)", IsNullable = true)]
public string F_LastModifyUserId { get; set; }
/// <summary>
/// 删除时间
/// </summary>
[SugarColumn(ColumnName="F_DeleteTime", ColumnDescription = "删除时间",ColumnDataType = "datetime", IsNullable = true)]
public DateTime? F_DeleteTime { get; set; }
/// <summary>
/// 删除人Id
/// </summary>
[SugarColumn(ColumnName="F_DeleteUserId", ColumnDescription = "删除人Id",ColumnDataType = "varchar(50)", IsNullable = true)]
public string F_DeleteUserId { get; set; }
/// <summary>
/// 组织机构ID
/// </summary>
[SugarColumn(ColumnName="F_OrganizationId", ColumnDescription = "组织机构ID",ColumnDataType = "varchar(50)", IsNullable = true)]
public string F_OrganizationId { get; set; }
/// <summary>
/// 订单编号(SC000000000000001)
/// </summary>
[SugarColumn(ColumnName="orderCode", ColumnDescription = "订单编号(SC000000000000001)",ColumnDataType = "varchar(255)", IsNullable = true)]
public string orderCode { get; set; }
/// <summary>
/// 订单类型(字典类型)
/// 1为陪诊订单
/// 2为护理订单
/// </summary>
[SugarColumn(ColumnName="orderType", ColumnDescription = "订单类型(字典类型)",ColumnDataType = "int(11)", IsNullable = true)]
public int? orderType { get; set; }
/// <summary>
/// 订单初始人员分配类型
/// 1系统分配
/// 2确定人员
/// </summary>
[SugarColumn(ColumnName = "initAllocationType", ColumnDescription = "订单初始人员分配类型", ColumnDataType = "int(11)", IsNullable = true)]
public int? initAllocationType { get; set; }
/// <summary>
/// 订单当前人员分配类型
/// 1系统分配
/// 2确定人员
/// </summary>
[SugarColumn(ColumnName = "curAllocationType", ColumnDescription = "订单当前人员分配类型", ColumnDataType = "int(11)", IsNullable = true)]
public int? curAllocationType { get; set; }
/// <summary>
/// 评价标识
/// 1待评价,2已评价
/// </summary>
[SugarColumn(ColumnName="commentState", ColumnDescription = "评价标识",ColumnDataType = "int(11)", IsNullable = true)]
public int? commentState { get; set; }
/// <summary>
/// 订单状态
/// </summary>
[SugarColumn(ColumnName="orderState", ColumnDescription = "订单状态",ColumnDataType = "int(11)", IsNullable = true)]
public int? orderState { get; set; }
/// <summary>
/// 交易截止时间
/// </summary>
[SugarColumn(ColumnName="orderPayTime", ColumnDescription = "交易截止时间",ColumnDataType = "datetime", IsNullable = true)]
public DateTime? orderPayTime { get; set; }
/// <summary>
/// 退款标识(1正常,2全部退款,3部分退款)
/// </summary>
[SugarColumn(ColumnName="refund", ColumnDescription = "退款标识(1正常,2全部退款,3部分退款)",ColumnDataType = "int(11)", IsNullable = true)]
public int? refund { get; set; }
/// <summary>
/// 退款发起时间
/// </summary>
[SugarColumn(ColumnName="refundTime", ColumnDescription = "退款发起时间",ColumnDataType = "datetime", IsNullable = true)]
public DateTime? refundTime { get; set; }
/// <summary>
/// 退款发起人用户ID
/// </summary>
[SugarColumn(ColumnName="refundUserId", ColumnDescription = "退款发起人用户ID",ColumnDataType = "varchar(255)", IsNullable = true)]
public string refundUserId { get; set; }
/// <summary>
/// 退款执行人用户ID
/// </summary>
[SugarColumn(ColumnName="refundExecuteUserId", ColumnDescription = "退款执行人用户ID",ColumnDataType = "varchar(255)", IsNullable = true)]
public string refundExecuteUserId { get; set; }
/// <summary>
/// 订单描述
/// </summary>
[SugarColumn(ColumnName="description", ColumnDescription = "订单描述",ColumnDataType = "varchar(255)", IsNullable = true)]
public string description { get; set; }
/// <summary>
/// 订单金额(元)
/// </summary>
[SugarColumn(ColumnName="total", ColumnDescription = "订单金额(元)",ColumnDataType = "decimal(18)", IsNullable = true)]
public decimal? total { get; set; }
/// <summary>
/// 服务地址表主键ID
/// </summary>
[SugarColumn(ColumnName="F_AddressId", ColumnDescription = "服务地址表主键ID",ColumnDataType = "varchar(255)", IsNullable = true)]
public string F_AddressId { get; set; }
/// <summary>
/// 备注
/// </summary>
[SugarColumn(ColumnName="remark", ColumnDescription = "备注",ColumnDataType = "varchar(255)", IsNullable = true)]
public string remark { get; set; }
/// <summary>
/// 当前服务人员用户ID
/// </summary>
[SugarColumn(ColumnName = "F_ServicesId", ColumnDescription = "当前服务人员用户ID", ColumnDataType = "varchar(50)", IsNullable = true)]
public string F_ServicesId { get; set; }
/// <summary>
/// 签到地址
/// 地图坐标
/// </summary>
[SugarColumn(ColumnName = "signAddress", ColumnDescription = "签到地址", ColumnDataType = "varchar(255)", IsNullable = true)]
public string signAddress { get; set; }
/// <summary>
/// 服务人员是否签到打卡
/// </summary>
[SugarColumn(ColumnName = "F_SignIn", ColumnDescription = "服务人员是否签到打卡", ColumnDataType = "tinyint(1)", IsNullable = true)]
public bool? F_SignIn { get; set; }
/// <summary>
/// 用户是否确认开始
/// </summary>
[SugarColumn(ColumnName = "F_ConfirmStart", ColumnDescription = "用户是否确认开始", ColumnDataType = "tinyint(1)", IsNullable = true)]
public bool? F_ConfirmStart { get; set; }
/// <summary>
/// 服务人员确认服务完成
/// </summary>
[SugarColumn(ColumnName = "F_ConfirmServiceCompleted", ColumnDescription = "服务人员确认服务完成", ColumnDataType = "tinyint(1)", IsNullable = true)]
public bool? F_ConfirmServiceCompleted { get; set; }
/// <summary>
/// 用户确认服务完成
/// </summary>
[SugarColumn(ColumnName = "F_UserConfirmServiceCompleted", ColumnDescription = "用户确认服务完成", ColumnDataType = "tinyint(1)", IsNullable = true)]
public bool? F_UserConfirmServiceCompleted { get; set; }
/// <summary>
/// 拒单人员IDS
/// 用于保存已拒单人员ID
/// </summary>
[SugarColumn(ColumnName = "rejectOrderUserIds", ColumnDescription = "拒单人员IDS", ColumnDataType = "varchar(500)", IsNullable = true)]
public string rejectOrderUserIds { get; set; }
/// <summary>
/// 限时时间类型
/// 枚举 LimitedTimeType
/// </summary>
[SugarColumn(ColumnName = "limitedTimeType", ColumnDescription = "限时时间类型", ColumnDataType = "int(11)", IsNullable = true)]
public int? limitedTimeType { get; set; }
/// <summary>
/// 限时时间
/// </summary>
[SugarColumn(ColumnName = "limitedTime", ColumnDescription = "限时时间", ColumnDataType = "datetime", IsNullable = true)]
public DateTime? limitedTime { get; set; }
/// <summary>
/// 拒单原因
/// </summary>
[SugarColumn( IsIgnore = true)]
public string RefuseReason { get; set; }
}
RabbitMq工具类,见顶部链接
生产者
cs
private static int countdownSeconds = 10;
private static Timer timer;
public async Task sendmessage(string code)
{
var attribute = typeof(BusinessorderEntity).GetAttribute<RabbitMqAttribute>();
countdownSeconds = 10;
Dictionary<string, object> Header = new Dictionary<string, object>
{
{ "x-delay", 10*1000 }//消息延迟发送时间10S
};
attribute.Header = Newtonsoft.Json.JsonConvert.SerializeObject(Header);
attribute.Exchange = "OrderTimeLimitExchange";
attribute.ExchangeType = "x-delayed-message";
_rabbitMqHelper.Publish<BusinessorderEntity>(attribute, new BusinessorderEntity()
{
orderCode = code,
orderPayTime = DateTime.Now.AddSeconds(10)//模拟订单过期时间,10秒后过期
},
true, //消息发送确认
(60 * 1000).ToString(),//消息过期时间60S
9);
timer = new Timer(Callback, null, 0, 1000); // 立即开始,每1000毫秒(1秒)触发一次
}
private static void Callback(object state)
{
countdownSeconds--;
Console.WriteLine($"剩余时间: {countdownSeconds} 秒");
if (countdownSeconds <= 0)
{
Console.WriteLine("倒计时结束!");
timer.Change(Timeout.Infinite, Timeout.Infinite);
}
}
消费者
消费者逻辑在RabbitMq事件类consumer.Received回调方法中
cs
using Jaina;
using Newtonsoft.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using WaterCloud.Code;
using WaterCloud.Domain.OrderManager;
namespace WaterCloud.Service.Event
{
public sealed class RabbitMQEventSourceStorer : IEventSourceStorer, IDisposable
{
/// <summary>
/// 内存通道事件源存储器
/// </summary>
private readonly Channel<IEventSource> _channel;
/// <summary>
/// 通道对象
/// </summary>
private readonly IModel _model;
/// <summary>
/// 连接对象
/// </summary>
private readonly IConnection _connection;
/// <summary>
/// 路由键
/// </summary>
private readonly string _routeKey;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="factory">连接工厂</param>
/// <param name="routeKey">路由键</param>
/// <param name="capacity">存储器最多能够处理多少消息,超过该容量进入等待写入</param>
public RabbitMQEventSourceStorer(ConnectionFactory factory, string routeKey, int capacity)
{
// 配置通道,设置超出默认容量后进入等待
var boundedChannelOptions = new BoundedChannelOptions(capacity)
{
FullMode = BoundedChannelFullMode.Wait
};
// 创建有限容量通道
_channel = Channel.CreateBounded<IEventSource>(boundedChannelOptions);
// 创建连接
_connection = factory.CreateConnection();
_routeKey = routeKey;
// 创建通道
_model = _connection.CreateModel();
//创建交换机参数
var arguments = new Dictionary<string, object>
{
{ "x-delayed-type", "direct" }
};
//创建延时交换机
_model.ExchangeDeclare(exchange: "OrderTimeLimitExchange", type: "x-delayed-message", arguments: arguments);
// 声明路由队列
_model.QueueDeclare(queue: routeKey, durable: true, exclusive: false, autoDelete: false, arguments: null);
//绑定队列和交换机
_model.QueueBind(queue: routeKey, exchange: "OrderTimeLimitExchange", routingKey: routeKey);
// 创建消息订阅者
var consumer = new EventingBasicConsumer(_model);
// 订阅消息并写入内存 Channel
consumer.Received += (ch, ea) =>
{
//消息回调方法
try
{
// 读取原始消息
var stringEventSource = Encoding.UTF8.GetString(ea.Body.ToArray());
// 转换为 IEventSource,这里可以选择自己喜欢的序列化工具,如果自定义了 EventSource,注意属性是可读可写
var eventSource = JsonConvert.DeserializeObject<BaseEventSource>(stringEventSource);
BusinessorderEntity order = JsonConvert.DeserializeObject<BusinessorderEntity>(stringEventSource);
if (DateTime.Now > order.orderPayTime)
{
Console.WriteLine("订单超时,需要变更订单状态");
// 写入内存管道存储器
_channel.Writer.WriteAsync(eventSource);
// 确认该消息已被消费
_model.BasicAck(ea.DeliveryTag, false);
}
else
{
// 异常:拒绝消息,重新入队
_model.BasicNack(ea.DeliveryTag, false, true);
Console.WriteLine($"{order.orderCode}订单未超时");
}
}
catch (Exception ex)
{
// 异常:拒绝消息,重新入队
_model.BasicNack(ea.DeliveryTag, false, true);
// 记录日志
Console.WriteLine($"消息处理异常:{ex.Message}");
}
};
// 启动消费者 设置为手动应答消息
_model.BasicConsume(routeKey, false, consumer);
}
/// <summary>
/// 将事件源写入存储器
/// </summary>
/// <param name="eventSource">事件源对象</param>
/// <param name="cancellationToken">取消任务 Token</param>
/// <returns><see cref="ValueTask"/></returns>
public async ValueTask WriteAsync(IEventSource eventSource, CancellationToken cancellationToken)
{
// 空检查
if (eventSource == default)
{
throw new ArgumentNullException(nameof(eventSource));
}
// 这里判断是否是 ChannelEventSource 或者 自定义的 EventSource
if (eventSource is ChannelEventSource source)
{
// 序列化,这里可以选择自己喜欢的序列化工具
var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(source));
// 发布
_model.BasicPublish("", _routeKey, null, data);
}
else
{
// 这里处理动态订阅问题
await _channel.Writer.WriteAsync(eventSource, cancellationToken);
}
}
/// <summary>
/// 从存储器中读取一条事件源
/// </summary>
/// <param name="cancellationToken">取消任务 Token</param>
/// <returns>事件源对象</returns>
public async ValueTask<IEventSource> ReadAsync(CancellationToken cancellationToken)
{
// 读取一条事件源
var eventSource = await _channel.Reader.ReadAsync(cancellationToken);
return eventSource;
}
/// <summary>
/// 释放非托管资源
/// </summary>
public void Dispose()
{
_model.Dispose();
_connection.Dispose();
}
}
}
使用RabbitMq的好处
当有延时消息发送到RabbitMq服务(之后简称"服务")时,如果服务因意外停止,延时消息会被保存,重启服务后消息依然存在
