RabbitMQ操作代码封装

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; }
    }
}
View Code

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();
                }
            }
        }
    }
}
View Code

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;
        }
    }
}
View Code

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);
        }
    }
}
View Code

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;
                }
            }
        }
    }
}
View Code

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();
            }
        }
    }
}
View Code
 
posted @ 2019-07-17 17:52  玻璃鱼儿  阅读(634)  评论(0编辑  收藏  举报