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

相关推荐
何中应2 小时前
在windows本地部署RabbitMQ
分布式·消息队列·rabbitmq
Wild API2 小时前
按任务轻重做模型分流的实战思路
分布式·微服务·架构
低客的黑调3 小时前
RabbitMQ-从入门到生产落地
分布式·rabbitmq
宸津-代码粉碎机3 小时前
Spring Boot 4.0虚拟线程实战续更预告:高阶技巧、监控排查与分布式场景落地指南
java·大数据·spring boot·分布式·后端·python
霖霖总总13 小时前
[Redis小技巧32]Redis分布式锁的至暗时刻:从原理演进到时钟跳跃的终极博弈
数据库·redis·分布式
ZC跨境爬虫13 小时前
Scrapy分布式爬虫(单机模拟多节点):豆瓣Top250项目设置与数据流全解析
分布式·爬虫·python·scrapy
ZC跨境爬虫16 小时前
通俗易懂讲解分布式爬虫基础概念(附Scrapy-Redis实操教程)
redis·分布式·爬虫·python·scrapy
小红的布丁1 天前
雪花算法:高并发场景下的分布式唯一ID生成方案解析
分布式
鲸能云1 天前
电力安全监管新规技术解读:分布式新能源电站数字化监控体系建设实践
分布式