1、Message.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace XFC.RabbitMQ.Domain { /// <summary> /// 消息实体 /// </summary> public class Message { /// <summary> /// 消息创建 /// </summary> /// <param name="value">值</param> /// <param name="headers">头信息</param> /// <param name="contentType">MIME content type,缺省值为 text/plain</param> public Message(string value, IDictionary<string, object> headers, string contentType) { Value = value; Headers = headers; ContentType = contentType; } /// <summary> /// 消息创建 /// </summary> /// <param name="value">值</param> /// <param name="headers">头信息</param> public Message(string value, IDictionary<string, object> headers) : this(value, headers, "text/plain") { } /// <summary> /// 消息创建 /// </summary> /// <param name="value">值</param> /// <param name="contentType">MIME content type</param> public Message(string value, string contentType) : this(value, null, contentType) { } /// <summary> /// 消息创建 /// </summary> /// <param name="value">值</param> public Message(string value) : this(value, null, "text/plain") { } /// <summary> /// 消息创建 /// </summary> public Message() : this("", null, "text/plain") { } /// <summary> /// 消息值 /// </summary> public string Value { get; set; } /// <summary> /// 消息头 /// </summary> public IDictionary<string, object> Headers { get; set; } /// <summary> /// MIME content type /// </summary> public string ContentType { get; set; } } }
2、RabbitMqPublisher.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using RabbitMQ.Client; using XFC.RabbitMQ.Domain; namespace XFC.RabbitMQ { public class RabbitMqPublisher { private readonly string _rabbitMqUri; /// <summary> /// 构造函数 /// </summary> /// <param name="rabbitMqUri">连接串,如 amqp://guest:guest@localhost:5672/</param> public RabbitMqPublisher(string rabbitMqUri) { this._rabbitMqUri = rabbitMqUri; } /// <summary> /// 创建连接 /// </summary> private IConnection CreateConnection() { var factory = new ConnectionFactory { Uri = new Uri(_rabbitMqUri) }; return factory.CreateConnection(); } /// <summary> /// 创建信道 /// </summary> private IModel CreateChannel(IConnection con, string exchangeName, string exchangeType, string queueName, string routeKey) { var channel = con.CreateModel(); channel.ExchangeDeclare(exchangeName, exchangeType, true, false, null); if (!string.IsNullOrEmpty(queueName)) { channel.QueueDeclare(queueName, true, false, false, null); //创建一个消息队列,用来存储消息 channel.QueueBind(queueName, exchangeName, routeKey, null); } channel.BasicQos(0, 3, true); //在非自动确认消息的前提下,如果一定数目的消息(通过基于consume或者channel设置Qos的值)未被确认前,不进行消费新的消息 return channel; } /// <summary> /// 发送ExchangeType类型为Direct的消息 /// </summary> /// <param name="exchangeName">交换机名称</param> /// <param name="routeKey">消息路由key</param> /// <param name="message">消息实体</param> /// <param name="queueName">缺省队列名(不存在则自动创建),设置后可避免消息发送后由于没有队列接收而丢失的问题</param> /// <returns></returns> public bool PublishDirectMessage(string exchangeName, string routeKey, Message message, string queueName = "") { return this.PublishMessage(exchangeName, ExchangeType.Direct, queueName, routeKey, new[] { message }); } /// <summary> /// 批量发送ExchangeType类型为Direct的消息 /// </summary> /// <param name="exchangeName">交换机名称</param> /// <param name="routeKey">消息路由key</param> /// <param name="messages">消息实体</param> /// <param name="queueName">缺省队列名(不存在则自动创建),设置后可避免消息发送后由于没有队列接收而丢失的问题</param> /// <returns></returns> public bool PublishDirectMessages(string exchangeName, string routeKey, IEnumerable<Message> messages, string queueName = "") { return this.PublishMessage(exchangeName, ExchangeType.Direct, queueName, routeKey, messages); } /// <summary> /// 发送ExchangeType类型为Fanout的消息 /// </summary> /// <param name="exchangeName">交换机名称</param> /// <param name="message">消息实体</param> /// <param name="queueName">缺省队列名(不存在则自动创建),设置后可避免消息发送后由于没有队列接收而丢失的问题</param> /// <returns></returns> public bool PublishFanoutMessage(string exchangeName, Message message, string queueName = "") { return this.PublishMessage(exchangeName, ExchangeType.Fanout, queueName, "", new[] { message }); } /// <summary> /// 批量发送ExchangeType类型为Fanout的消息 /// </summary> /// <param name="exchangeName">交换机名称</param> /// <param name="messages">消息实体</param> /// <param name="queueName">缺省队列名(不存在则自动创建),设置后可避免消息发送后由于没有队列接收而丢失的问题</param> /// <returns></returns> public bool PublishFanoutMessages(string exchangeName, IEnumerable<Message> messages, string queueName = "") { return this.PublishMessage(exchangeName, ExchangeType.Fanout, queueName, "", messages); } private bool PublishMessage(string exchangeName, string exchangeType, string queueName, string routeKey, IEnumerable<Message> messages) { using (var con = CreateConnection()) { using (var channel = CreateChannel(con, exchangeName, exchangeType, queueName, routeKey)) { channel.ConfirmSelect();//启用消息发送确认机制 foreach (var message in messages) { var body = Encoding.UTF8.GetBytes(message.Value); var properties = channel.CreateBasicProperties(); properties.Persistent = true; //使消息持久化 properties.Headers = message.Headers; properties.ContentType = string.IsNullOrEmpty(message.ContentType) ? "text/plain" : message.ContentType; channel.BasicPublish(exchangeName, routeKey, properties, body); } return channel.WaitForConfirms(); } } } } }
3、RabbitMqQuery.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using RabbitMQ.Client; using XFC.RabbitMQ.Domain; namespace XFC.RabbitMQ { public class RabbitMqQuery { private readonly string _rabbitMqUri; /// <summary> /// 构造函数 /// </summary> /// <param name="rabbitMqUri">连接串,如 amqp://guest:guest@localhost:5672/</param> public RabbitMqQuery(string rabbitMqUri) { this._rabbitMqUri = rabbitMqUri; } /// <summary> /// 创建连接 /// </summary> private IConnection CreateConnection() { var factory = new ConnectionFactory { Uri = new Uri(_rabbitMqUri) }; return factory.CreateConnection(); } /// <summary> /// 拉取队列中的数据 /// </summary> /// <param name="queueName">队列名</param> /// <returns></returns> public Message GetMessage(string queueName) { using (var con = this.CreateConnection()) { var channel = con.CreateModel(); var rs = channel.BasicGet(queueName, true); return ResultToMessage(rs); } } /// <summary> /// 批量拉取队列中的数据 /// </summary> /// <param name="queueName">队列名</param> /// <param name="queryCount">拉取数据的条数,默认为1</param> /// <returns></returns> public Message[] GetMessages(string queueName, int queryCount = 1) { if (queryCount <= 0){ queryCount = 1; } var msgLst = new List<Message>(); using (var con = this.CreateConnection()) { var channel = con.CreateModel(); for (int i = 0; i < queryCount; i++) { var rs = channel.BasicGet(queueName, true); if (rs != null) { msgLst.Add(ResultToMessage(rs)); } else { break; } } } return msgLst.ToArray(); } private Message ResultToMessage(BasicGetResult rs) { var msg = new Message(); if (rs != null) { var body = rs.Body; msg.Value = Encoding.UTF8.GetString(body); msg.ContentType = rs.BasicProperties.ContentType; msg.Headers = rs.BasicProperties.Headers; } return msg; } } }
4、RabbitMqListener.cs
using RabbitMQ.Client; using RabbitMQ.Client.Events; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using RabbitMQ.Client.Exceptions; using XFC.Log; using XFC.RabbitMQ.Domain; namespace XFC.RabbitMQ { /// <summary> /// RabbitMq消息监听器 /// </summary> public class RabbitMqListener : IDisposable { private ConnectionFactory _factory; private IConnection _con; private IModel _channel; private EventingBasicConsumer _consumer; private readonly string _rabbitMqUri; private readonly string _queueName; private Func<Message, bool> _messageHandler; /// <summary> /// 释放标记 /// </summary> private bool disposed; ~RabbitMqListener() { Dispose(false); } /// <summary> /// RabbitMQ消息监听器 /// </summary> /// <param name="rabbitMqUri">连接串,如 amqp://guest:guest@localhost:5672/</param> /// <param name="queueName">要监听的队列</param> public RabbitMqListener(string rabbitMqUri, string queueName) { this._rabbitMqUri = rabbitMqUri; this._queueName = queueName; } /// <summary> /// 创建连接 /// </summary> private void CreateConnection() { _factory = new ConnectionFactory { Uri = new Uri(_rabbitMqUri), RequestedHeartbeat = 20,//与服务器协商使用的心跳超时间隔(以秒为单位)。 AutomaticRecoveryEnabled = true,//开启网络异常重连机制 NetworkRecoveryInterval = TimeSpan.FromSeconds(10),//设置每10s重连一次网络 TopologyRecoveryEnabled = true //开启重连后恢复拓扑(交换,队列,绑定等等)。 }; _con = _factory.CreateConnection(); _con.ConnectionShutdown += (sender, e) => ReMessageListen();//掉线重新连接并监听队列消息 } /// <summary> /// 创建信道 /// </summary> private void CreateChannel() { _channel = _con.CreateModel(); _channel.BasicQos(0, 3, true); //在非自动确认消息的前提下,如果一定数目的消息(通过基于consume或者channel设置Qos的值)未被确认前,不进行消费新的消息 } private Message ResultToMessage(BasicDeliverEventArgs rs) { var msg = new Message(); if (rs != null) { var body = rs.Body; msg.Value = Encoding.UTF8.GetString(body); msg.ContentType = rs.BasicProperties.ContentType; msg.Headers = rs.BasicProperties.Headers; } return msg; } /// <summary> /// 监听队列消息 /// </summary> /// <param name="messageHandler">消息处理器,当监测到队列消息时回调该处理器</param> /// <returns>监听状态</returns> public bool MessageListen(Func<Message, bool> messageHandler) { try { this.CreateConnection(); this.CreateChannel(); _consumer = new EventingBasicConsumer(_channel); //基于事件的消息推送方式 _consumer.Received += (sender, e) => { var message = this.ResultToMessage(e); if (messageHandler != null) { this._messageHandler = messageHandler; try { var isOk = this._messageHandler(message); if (isOk) { _channel.BasicAck(e.DeliveryTag, false); } } catch (Exception ex) { LoggerManager.ErrorLog.Error("消息处理器执行异常:" + ex.Message, ex); } } }; _channel.BasicConsume(_queueName, false, _consumer); //手动确认 return true; } catch (Exception ex) { LoggerManager.ErrorLog.Error("尝试监听队列消息出现错误:" + ex.Message, ex); } return false; } private void ReMessageListen() { try { //清除连接及频道 CleanupResource(); var mres = new ManualResetEventSlim(false); //初始化状态为false while (!mres.Wait(3000)) //每3秒监测一次状态,直到状态为true { if (MessageListen(_messageHandler)) { mres.Set(); //设置状态为true并跳出循环 } } } catch (Exception ex) { LoggerManager.ErrorLog.Error("尝试连接RabbitMQ服务器出现错误:" + ex.Message, ex); } } /// <summary> /// 清理资源 /// </summary> private void CleanupResource() { if (_channel != null && _channel.IsOpen) { try { _channel.Close(); } catch (Exception ex) { LoggerManager.ErrorLog.Error("尝试关闭RabbitMQ信道遇到错误", ex); } _channel = null; } if (_con != null && _con.IsOpen) { try { _con.Close(); } catch (Exception ex) { LoggerManager.ErrorLog.Error("尝试关闭RabbitMQ连接遇到错误", ex); } _con = null; } } protected virtual void Dispose(bool disposing) { if (disposed) { return; } CleanupResource(); disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } }
5、RabbitMqDelayPublisher
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using RabbitMQ.Client; using XFC.Exceptions; using XFC.RabbitMQ.Domain; namespace XFC.RabbitMQ { public class RabbitMqDelayPublisher { private readonly string _rabbitMqUri; private readonly string _dlxExchangeName; private readonly string _dlxQueueName; private readonly string _dlxRouteKey; /// <summary> /// 构造函数 /// </summary> /// <param name="rabbitMqUri">连接串,如 amqp://guest:guest@localhost:5672/</param> public RabbitMqDelayPublisher(string rabbitMqUri) { this._rabbitMqUri = rabbitMqUri; } /// <summary> /// 构造函数 /// </summary> /// <param name="rabbitMqUri">连接串,如 amqp://guest:guest@localhost:5672/</param> /// <param name="dlxExchangeName">死信队列交换机名称</param> /// <param name="dlxQueueName">死信队列名称</param> /// <param name="dlxRouteKey">死信队列消息路由key</param> public RabbitMqDelayPublisher(string rabbitMqUri, string dlxExchangeName, string dlxQueueName, string dlxRouteKey) : this(rabbitMqUri) { this._dlxExchangeName = dlxExchangeName; this._dlxQueueName = dlxQueueName; this._dlxRouteKey = dlxRouteKey; } /// <summary> /// 创建连接 /// </summary> private IConnection CreateConnection() { var factory = new ConnectionFactory { Uri = new Uri(_rabbitMqUri) }; return factory.CreateConnection(); } /// <summary> /// 创建信道 /// </summary> private IModel CreateChannel(IConnection con, string exchangeName, string exchangeType, string queueName, string routeKey, int delayTime) { if (string.IsNullOrEmpty(queueName)) { throw new XFCException("queueName不能为空"); } var channel = con.CreateModel(); var dlxExchangeName = string.IsNullOrEmpty(this._dlxExchangeName) ? string.Format("dlx_{0}", exchangeName) : this._dlxExchangeName; var dlxQueueName = string.IsNullOrEmpty(this._dlxQueueName) ? string.Format("dlx_{0}", queueName) : this._dlxQueueName; var dlxRouteKey = string.IsNullOrEmpty(this._dlxRouteKey) ? (string.IsNullOrEmpty(routeKey) ? Guid.NewGuid().ToString().Replace("-", "") : string.Format("dlx_{0}", routeKey)) : this._dlxRouteKey; channel.ExchangeDeclare(dlxExchangeName, ExchangeType.Direct, true, false, null); channel.QueueDeclare(dlxQueueName, true, false, false, null); channel.QueueBind(dlxQueueName, dlxExchangeName, dlxRouteKey, null); var args = new Dictionary<string, object> { {"x-message-ttl", delayTime}, {"x-dead-letter-exchange", dlxExchangeName}, {"x-dead-letter-routing-key", dlxRouteKey} }; channel.ExchangeDeclare(exchangeName, exchangeType, true, false, null); channel.QueueDeclare(queueName, true, false, false, args); channel.QueueBind(queueName, exchangeName, routeKey, null); channel.BasicQos(0, 1, true); return channel; } /// <summary> /// 发送ExchangeType类型为Direct的消息 /// </summary> /// <param name="exchangeName">交换机名称</param> /// <param name="routeKey">消息路由key</param> /// <param name="expireTime">过期时间,单位为秒</param> /// <param name="message">消息实体</param> /// <param name="queueName">队列名(必填)</param> /// <returns></returns> public bool PublishDirectMessage(string exchangeName, string queueName, string routeKey, int expireTime, Message message) { return this.PublishMessage(exchangeName, ExchangeType.Direct, queueName, routeKey, expireTime, new[] { message }); } /// <summary> /// 批量发送ExchangeType类型为Direct的消息 /// </summary> /// <param name="exchangeName">交换机名称</param> /// <param name="routeKey">消息路由key</param> /// <param name="expireTime">过期时间,单位为秒</param> /// <param name="messages">消息实体</param> /// <param name="queueName">队列名(必填)</param> /// <returns></returns> public bool PublishDirectMessages(string exchangeName, string queueName, string routeKey, int expireTime, IEnumerable<Message> messages) { return this.PublishMessage(exchangeName, ExchangeType.Direct, queueName, routeKey, expireTime, messages); } /// <summary> /// 发送ExchangeType类型为Fanout的消息 /// </summary> /// <param name="exchangeName">交换机名称</param> /// <param name="expireTime">过期时间,单位为秒</param> /// <param name="message">消息实体</param> /// <param name="queueName">队列名(必填)</param> /// <returns></returns> public bool PublishFanoutMessage(string exchangeName, string queueName, int expireTime, Message message) { return this.PublishMessage(exchangeName, ExchangeType.Fanout, queueName, "", expireTime, new[] { message }); } /// <summary> /// 批量发送ExchangeType类型为Fanout的消息 /// </summary> /// <param name="exchangeName">交换机名称</param> /// <param name="expireTime">过期时间,单位为秒</param> /// <param name="messages">消息实体</param> /// <param name="queueName">队列名(必填)</param> /// <returns></returns> public bool PublishFanoutMessages(string exchangeName, string queueName, int expireTime, IEnumerable<Message> messages) { return this.PublishMessage(exchangeName, ExchangeType.Fanout, queueName, "", expireTime, messages); } private bool PublishMessage(string exchangeName, string exchangeType, string queueName, string routeKey, int expireTime, IEnumerable<Message> messages) { using (var con = CreateConnection()) { using (var channel = CreateChannel(con, exchangeName, exchangeType, queueName, routeKey, expireTime * 1000)) { channel.ConfirmSelect();//启用消息发送确认机制 foreach (var message in messages) { var body = Encoding.UTF8.GetBytes(message.Value); var properties = channel.CreateBasicProperties(); properties.Persistent = true; //使消息持久化 properties.Headers = message.Headers; properties.ContentType = string.IsNullOrEmpty(message.ContentType) ? "text/plain" : message.ContentType; channel.BasicPublish(exchangeName, routeKey, properties, body); } var state = channel.WaitForConfirms(); return state; } } } } }
6、Test
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using RabbitMQ.Client; using XFC.RabbitMQ.Domain; namespace XFC.RabbitMQ.Test { class Program { private const string RabbitHostUri = "amqp://guest:guest@localhost:5672/"; private const string ExchangeName = "xfc.mq.test"; private const string QueueName = "xfc.mq.test.queue"; private const string RouteKey = "xfc.mq.test.key"; static void Main(string[] args) { var publisher = new XFC.RabbitMQ.RabbitMqPublisher(RabbitHostUri); var dic = new Dictionary<string, object>(); dic.Add("uniquekey", "s34sdf3423234523sdfdsgf"); dic.Add("callbackurl", "http://wwww.1234.com/callback"); var ms =new List<Message>(); for (int i = 0; i < 1000; i++) { ms.Add(new Message("hello...", dic, "application/json")); } publisher.PublishDirectMessages(ExchangeName, RouteKey, ms, QueueName); Console.WriteLine("is ok"); Console.ReadKey(); var mqQuery = new XFC.RabbitMQ.RabbitMqQuery(RabbitHostUri); var ss = mqQuery.GetMessages(QueueName, 1000); foreach (var s in ss) { Console.WriteLine(s.Value); Console.WriteLine(s.ContentType); foreach (var header in s.Headers) { Console.WriteLine(header.Key + ":" + Encoding.UTF8.GetString((Byte[])header.Value)); } } Console.ReadKey(); using (var mqListener = new XFC.RabbitMQ.RabbitMqListener(RabbitHostUri, QueueName)) { mqListener.MessageListen(msg => { Console.WriteLine(msg.Value); return true; }); Console.WriteLine("按任意键退出程序..."); Console.ReadKey(); } } } }