【.net core】【RabbitMq】rabbitmq在.net core中的简单使用

使用示例:假设订单需要设置订单时效为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,说明插件已生效。

    bash 复制代码
    Microsoft 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>

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服务(之后简称"服务")时,如果服务因意外停止,延时消息会被保存,重启服务后消息依然存在

相关推荐
添砖java_85714 小时前
基于RabbitMQ实现的轻量队列测试报告
分布式·rabbitmq
Zhu75815 小时前
使用腾讯CNB构建Hadoop定制容器镜像
大数据·hadoop·分布式
TTBIGDATA16 小时前
【Ambari Plus】13.Spark 安装
大数据·hadoop·分布式·spark·ambari·sqoop·ambari plus
sunxunyong18 小时前
Hadoop租户创建
大数据·hadoop·分布式
得物技术18 小时前
得物 OceanBase 落地实践
数据库·分布式·架构
GuangdongTong19 小时前
基于RabbitMQ实现延迟任务
后端·rabbitmq
heimeiyingwang20 小时前
【架构实战】分布式ID生成:从自增ID到雪花算法
分布式·架构
微三云 - 廖会灵 (私域系统开发)2 天前
电商系统微服务拆分实战:从单体到Spring Cloud的分布式事务踩坑与补偿方案
分布式·spring cloud·微服务
ACP广源盛139246256732 天前
破局 PCIe 4.0 交换瓶颈@ACP#IX8024 / ASM58024参数及应用对比
大数据·人工智能·分布式·嵌入式硬件
淡然的煎蛋2 天前
开始使用RabbitMQ
分布式·rabbitmq·ruby