/******************************** * * 作者:吴隆健 * 创建时间: 2019/6/24 14:22:26 * ********************************/ #if RABBITMQ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Text; using System.Threading; using RabbitMQ.Client; using RabbitMQ.Client.Events; namespace MrWu.RabbitMQ { /// /// rabbit管理 /// public class RabbitManager { /// /// 配置 /// public RabbitConfig config { get; private set; } /// /// 发送失败 /// public event MessageHandle SendResult; /// /// 未路由成功事件 /// public event MessageHandle notRoutingOk; /// /// 构造 /// public RabbitManager() { } /// /// 未确认的数量 /// public int NoAckCount { get { return waitAck.Count; } } /// /// 启动rabbit管理 /// /// public void Start(RabbitConfig config = null) { if (connection != null) { Debug.Debug.Error("已启动过!"); return; } if (config != null) this.config = config; if (this.config == null) throw new ArgumentNullException("config is null"); Connection(); } /// /// 链接 /// public IConnection connection { get; private set; } /// /// 生产者信道 /// private IModel producer { get; set; } /// /// 获取信息的通道 /// private IModel infoChannel { get; set; } /// /// 等待发送确认的消息 /// private ConcurrentDictionary waitAck { get; } = new ConcurrentDictionary(); /// /// 生产者锁 /// private readonly object lockobj = new object(); /// /// 链接rabbit /// private void Connection() { ConnectionFactory factory = new ConnectionFactory(); factory.UserName = config.username; factory.Password = config.password; factory.VirtualHost = config.vhost; factory.HostName = config.host; factory.Port = config.port; factory.AutomaticRecoveryEnabled = true; if (connection != null) { connection.ConnectionBlocked -= ConnectionBlockedEventHandler; connection.ConnectionUnblocked -= ConnectionUnblockedEventHandler; } connection = factory.CreateConnection(); connection.ConnectionBlocked += ConnectionBlockedEventHandler; connection.ConnectionUnblocked += ConnectionUnblockedEventHandler; Debug.Debug.Log("链接创建生产者!"); CreateProducer(); DeclareExchange(config.exchanges); DeclareQueue(config.queues); Bind(config.binds); } /// /// 创建生产者 /// private void CreateProducer() { try { Debug.Debug.Log("创建生产者:" + Thread.CurrentThread.ManagedThreadId); producer = connection.CreateModel(); producer.ConfirmSelect(); producer.ModelShutdown += ModelShutdown; producer.BasicReturn += MessageReturn; producer.BasicAcks += MessageAck; producer.BasicNacks += MessageNAck; } catch (Exception e) { Debug.Debug.Error("创建生产者出错!" + e); } } /// /// 消息阻塞!!! /// /// /// private void ConnectionBlockedEventHandler(IConnection sender, ConnectionBlockedEventArgs args) { Debug.Debug.Fatal("RabbitMQ 消息阻塞,链接被禁用!" + args.Reason); } /// /// 消息阻塞被解除! /// /// private void ConnectionUnblockedEventHandler(IConnection sender) { Debug.Debug.Fatal("RabbitMQ 消息解除阻塞!"); } /// /// 链接禁用! /// public void ConnectionBlocked() { if (connection == null) return; connection.HandleConnectionBlocked("auto blocked"); } /// /// 解除链接禁用 /// public void ConnectionUnBlocked() { if (connection == null) return; connection.HandleConnectionUnblocked(); } /// /// 创建信息通道 /// private void CreateInfoChannel() { try { infoChannel = connection.CreateModel(); infoChannel.ModelShutdown += ModelShutdown; } catch (Exception e) { Debug.Debug.Error("创建信息获取通道出错!" + e.ToString()); } } /// /// 消息发送成功 /// /// /// private void MessageAck(IModel model, BasicAckEventArgs args) { DoAck(model, args.DeliveryTag, args.Multiple, true); } /// /// /// /// /// /// /// private void DoAck(IModel model, ulong deliverytag, bool multiple, bool success) { void DoAck(Message _msg, bool _success) { _msg.success = success; if (_msg.SendResult != null) _msg.SendResult(_msg); else SendResult?.Invoke(_msg); if (_msg.isAutoFree) MessagePool.PutMessage(ref _msg); } Message msg; //同步确认的消息也会到这里来!!! if (!multiple) { if (waitAck.TryRemove(deliverytag, out msg)) {//拿到 DoAck(msg, success); } } else { List keys = new List(); foreach (var item in waitAck) { if (item.Value.deliveryTag <= deliverytag) keys.Add(item.Value.deliveryTag); } foreach (var item in keys) { if (waitAck.TryRemove(item, out msg)) { DoAck(msg, success); } } } } /// /// 消息未被发送 /// /// /// private void MessageNAck(IModel model, BasicNackEventArgs args) { DoAck(model, args.DeliveryTag, args.Multiple, false); } /// /// 信道断开 /// /// /// private void ModelShutdown(IModel model, ShutdownEventArgs reason) { Debug.Debug.Warning("信道断开事件:" + Thread.CurrentThread.ManagedThreadId); if (producer != null && producer.IsClosed) { Debug.Debug.Info("剩余确认数量:" + waitAck.Count); // producer.BasicNack producer.ModelShutdown -= ModelShutdown; producer.BasicReturn -= MessageReturn; producer.BasicAcks -= MessageAck; producer.BasicNacks -= MessageNAck; waitAck.Clear(); producer = null; } else if (infoChannel != null && infoChannel.IsClosed) infoChannel = null; Debug.Debug.Error("信道断开:" + reason); } /// /// 消息被返回 /// /// /// private void MessageReturn(IModel model, BasicReturnEventArgs args) { if (args.BasicProperties != null) { string msgid = args.BasicProperties.MessageId; Message msg = MessagePool.GetMessage(msgid);// new Message(msgid); msg.noAck = true; msg.body = args.Body; msg.channel = model; msg.exchange = args.Exchange; msg.routingkey = args.RoutingKey; msg.userData = args.BasicProperties.Headers; notRoutingOk?.Invoke(msg); } else { Debug.Debug.Error("怎么可能BasicProperties 不存在"); } Debug.Debug.Error("消息被撤回,ReplyText:" + args.Exchange + "," + args.RoutingKey + "," + args.ReplyText + ",ReplyCode:" + args.ReplyCode + ",body:" + Encoding.UTF8.GetString(args.Body)); } /// /// 声明路由 /// public void DeclareExchange(ExchangeInfo[] infos) { if (infos != null) { int len = infos.Length; for (int i = 0; i < len; i++) { ExchangeInfo exchange = infos[i]; IDictionary properties = new Dictionary(); if (!string.IsNullOrEmpty(exchange.alternate_exchange)) { properties.Add("alternate-exchange", exchange.alternate_exchange); } if (exchange.delExists) DelExchange(exchange.name); //Debug.Debug.Log("进入锁44444"+Thread.CurrentThread.ManagedThreadId); lock (lockobj) { producer.ExchangeDeclare(exchange.name, exchange.type, exchange.durable, exchange.autodelete, properties); } //Debug.Debug.Log("退出锁4444"); } } } /// /// 声明队列 /// public QueueDeclareOk DeclareQueue() { QueueDeclareOk result = null; //Debug.Debug.Log("进入锁3333"+Thread.CurrentThread.ManagedThreadId); lock (lockobj) { try { result = producer.QueueDeclare(); } catch { return null; } } //Debug.Debug.Log("退出锁3333"); return result; } /// ///声明队列 /// public void DeclareQueue(QueueInfo[] infos) { if (infos != null) { int len = infos.Length; for (int i = 0; i < len; i++) { QueueInfo queue = infos[i]; IDictionary properties = new Dictionary(); if (!string.IsNullOrEmpty(queue.x_dead_letter_exchange)) { properties.Add("x-dead-letter-exchange", queue.x_dead_letter_exchange); } if (!string.IsNullOrEmpty(queue.x_dead_routing_key)) { properties.Add("x-dead-routing-key", queue.x_dead_routing_key); } if (queue.x_expires > 0) { properties.Add("x-expires", queue.x_expires); } if (queue.x_max_length > 0) { properties.Add("x-max-length", queue.x_max_length); } if (queue.x_max_length_bytes > 0) { properties.Add("x-max-length-bytes", queue.x_max_length_bytes); } if (queue.x_max_priority > 0) { properties.Add("x-max-priority", queue.x_max_priority); } if (queue.x_message_ttl > 0) { properties.Add("x-message-ttl", queue.x_message_ttl); } if (!string.IsNullOrEmpty(queue.x_overflow)) { properties.Add("x-overflow", queue.x_overflow); } if (!string.IsNullOrEmpty(queue.x_queue_mode)) { properties.Add("x-queue-mode", queue.x_queue_mode); } if (queue.delExists) DelQueue(queue.name); //Debug.Debug.Log("进入锁7454354354"+Thread.CurrentThread.ManagedThreadId); lock (lockobj) { producer.QueueDeclare(queue.name, queue.durable, queue.exclusive, queue.autodelete, properties); if (queue.startClear) producer.QueuePurge(queue.name); } //Debug.Debug.Log("退出锁7454354354"+Thread.CurrentThread.ManagedThreadId); } } } /// /// 绑定 /// public void Bind(BindInfo[] infos) { if (infos != null) { int len = infos.Length; for (int i = 0; i < len; i++) { BindInfo bind = infos[i]; switch (bind.bindType) { case "queue": //Debug.Debug.Log("进入锁asdasfasf"+Thread.CurrentThread.ManagedThreadId); lock (lockobj) { producer.QueueBind(bind.destination, bind.source, bind.routingkey); } //Debug.Debug.Log("退出锁asdasfasf"+Thread.CurrentThread.ManagedThreadId); break; case "exchange": //Debug.Debug.Log("进入锁asdsafasfasd"+Thread.CurrentThread.ManagedThreadId); lock (lockobj) { producer.ExchangeBind(bind.destination, bind.source, bind.routingkey); } //Debug.Debug.Log("退出锁asdsafasfasd"+Thread.CurrentThread.ManagedThreadId); break; default: throw new ArgumentException("BindType value is not queue/exchange"); } } } } /// /// 获取队列信息 /// /// 名称 /// 队列信息 public QueueDeclareOk GetQueueInfo(string name) { try { IModel channel = connection.CreateModel(); var result = channel.QueueDeclarePassive(name); return result; } catch { return null; } } /// /// 交换机存在不存在 /// /// 交换机名称 /// true 表示存在 false 可能是网络故障,或者不存在 public bool ExchangeExists(string name) { try { IModel channel = connection.CreateModel(); channel.ExchangeDeclarePassive(name); return true; } catch { return false; } } private readonly ConcurrentBag rpcs = new ConcurrentBag(); /// /// 创建RPC客户端 /// /// public RpcClient CreateRpcClient(ushort prefetchCount = 3) { var rpc = new RpcClient(config, prefetchCount); rpcs.Add(rpc); return rpc; } /// /// 创建信道 /// /// internal IModel CreateIModel() { return connection.CreateModel(); } /// /// 获取消息属性 /// /// public IBasicProperties GetBasicProperties() { try { IBasicProperties result = null; //Debug.Debug.Log("进入锁6666" + Thread.CurrentThread.ManagedThreadId); lock (lockobj) { //Debug.Debug.Log("生产者:" + (producer == null)); if (producer == null) CreateProducer(); if (producer == null) result = null; else result = producer.CreateBasicProperties(); } return result; } catch (Exception e) { Debug.Debug.Error("获取消息属性出错:" + e.ToString()); return null; } finally { //Debug.Debug.Log("退出锁6666"); } } /// /// 无返回值的发送,按配置来决定是异步还是同步确认 /// public void BasicPublish(Message msg, bool isAutoFree) { BasicPublish(msg, config.AsynConfirm, isAutoFree); } /// /// 有返回值的发送_必须是同步,马上需要知道结果 /// /// public bool BasicPublish_Result(Message msg, bool isAutoFree) { return BasicPublish(msg, false, isAutoFree); } /// /// 发送消息 /// /// 消息 /// isAsyn 是否异步 /// 是否自动释放 private bool BasicPublish(Message msg, bool isAsyn, bool isAutoFree) { if (msg == null || msg.body == null || msg.body.Length == 0) { Debug.Debug.Error("发送数据为空!" + new System.Diagnostics.StackTrace().ToString()); return false; } msg.isAutoFree = isAutoFree; bool isNotice = false; bool result = false; try { //Debug.Debug.Log("进入锁1111" + Thread.CurrentThread.ManagedThreadId); lock (lockobj) { //Debug.Debug.Log("处理发送!" + (producer == null)); if (producer == null) { CreateProducer(); } //Debug.Debug.Log("111"); if (producer == null) { isNotice = true; msg.success = false; } else { msg.channel = producer; msg.noAck = true; msg.deliveryTag = producer.NextPublishSeqNo; if (msg.propertis == null) msg.propertis = producer.CreateBasicProperties(); msg.propertis.MessageId = msg.id; msg.propertis.Headers = msg.userData; //持久化 msg.propertis.SetPersistent(msg.durable); //Debug.Debug.Info($"读取到过期时间:{msg.propertis.Expiration}"); if (!msg.durable) { msg.propertis.Expiration = "3000"; } //Debug.Debug.Log("发送消息:" + msg.bodystr + "," + msg.exchange + "," + msg.propertis + "," + msg.mandatory); producer.BasicPublish(msg.exchange, msg.routingkey, msg.mandatory, false, msg.propertis, msg.body); if (!isAsyn) {//同步 //Debug.Debug.Log("同步等待!"); msg.success = producer.WaitForConfirms(); result = msg.success; if (!msg.success) { Console.WriteLine(msg.success); } isNotice = true; } else { //异步 //添加到列表 if (producer.IsClosed) { isNotice = true; msg.success = false; } else { //Debug.Debug.Log("异步发送:" + msg.deliveryTag); //Debug.Debug.Log("得到数据:" + waitAck.Count); waitAck.GetOrAdd(msg.deliveryTag, msg); //Debug.Debug.Log("得到数据:" + waitAck.Count); } } } } } catch (Exception e) { Debug.Debug.Warning("发送消息失败:" + e.ToString() + Environment.NewLine + msg.ToString()); isNotice = true; msg.success = false; } if (isNotice) { if (msg.SendResult != null) msg.SendResult(msg); else SendResult?.Invoke(msg); if (msg.isAutoFree) MessagePool.PutMessage(ref msg); } return result; } /// /// 删除队列 /// /// 队列名称 /// 是否没有用户才删除 /// 是否是空队列才删除 /// uint 0表示删除失败 大于等于1表示删除成功 队列中消息数量+1 public uint DelQueue(string queue, bool ifUnused = false, bool ifEmpty = false) { IModel model = connection.CreateModel(); uint result = 0; try { result = model.QueueDelete(queue, ifUnused, ifEmpty); result += 1; } catch (Exception e) { Debug.Debug.Warning("删除队列异常:" + e.ToString()); } return result; } /// /// 删除交换机 /// /// 交换机 /// 是否没用户才删除 /// true 表示删除成功 false 队列不存在 public bool DelExchange(string exchange, bool ifUnused = false) { IModel model = connection.CreateModel(); try { model.ExchangeDelete(exchange, ifUnused); } catch (Exception e) { Debug.Debug.Warning("删除交换机异常:" + e.ToString()); return false; } return true; } /// /// 获取消息 /// /// 队列 /// true 表示自动确认 false 表示手动确认 /// public Message GetMessage(string queue, bool noAck = true) { //Debug.Debug.Log("进入锁2222" + Thread.CurrentThread.ManagedThreadId); lock (lockobj) { if (infoChannel == null) CreateInfoChannel(); if (infoChannel == null) return null; } //Debug.Debug.Log("退出锁2222"); IModel key = infoChannel; Message msg = null; lock (key) { try { BasicGetResult bgr = key.BasicGet(queue, noAck); if (bgr != null) { string msgid = null; IDictionary userdata = null; if (bgr.BasicProperties == null) { Debug.Debug.Error("这个消息竟然没有属性!"); } else { msgid = bgr.BasicProperties.MessageId; userdata = bgr.BasicProperties.Headers; } msg = MessagePool.GetMessage(msgid);// new Message(msgid); msg.noAck = noAck; msg.exchange = bgr.Exchange; msg.routingkey = bgr.RoutingKey; msg.propertis = bgr.BasicProperties; msg.body = bgr.Body; msg.deliveryTag = bgr.DeliveryTag; msg.channel = key; msg.redelivered = bgr.Redelivered; msg.messageCount = bgr.MessageCount; msg.userData = userdata; } } catch (Exception e) { Debug.Debug.Error("获取消息出错:" + e.ToString()); return null; } } return msg; } /// /// 添加消费者 /// /// 消费者 public void AddConsumer(Consumer consumer) { IModel model = connection.CreateModel(); consumer.channel = model; if (string.IsNullOrEmpty(consumer.consumertag)) consumer.consumertag = Guid.NewGuid().ToString(); consumer.consumertag = model.BasicConsume(consumer.queue, consumer.noAck, consumer.consumertag, consumer.noLoacl, consumer.exclusive, consumer.arguments, new BasicConsumer(consumer)); if (consumer.prefetchCount > 0) { model.BasicQos(0, consumer.prefetchCount, true); } } /// /// 移出消费者 /// /// public void RemoveConsumer(Consumer consumer) { if (consumer == null || consumer.channel == null || consumer.channel.IsClosed) return; consumer.channel.BasicCancel(consumer.consumertag); } /// /// 关闭 /// public void Close() { if (connection != null) { connection.Dispose(); connection = null; } foreach (var item in rpcs) { item.Close(); } } /// /// 消费者类 /// private class BasicConsumer : DefaultBasicConsumer { public Consumer consumer; public BasicConsumer(Consumer consumer) { this.consumer = consumer; } public override void HandleBasicCancel(string consumerTag) { base.HandleBasicCancel(consumerTag); } public override void HandleBasicConsumeOk(string consumerTag) { base.HandleBasicConsumeOk(consumerTag); } public override void HandleBasicCancelOk(string consumerTag) { base.HandleBasicCancelOk(consumerTag); } public override void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, IBasicProperties properties, byte[] body) { string msgid = null; IDictionary userdata = null; if (properties == null) { Debug.Debug.Error("消费者接收消息异常,没有收到消息id"); } else { msgid = properties.MessageId; userdata = properties.Headers; } Message message = MessagePool.GetMessage(msgid); // new Message(msgid); message.exchange = exchange; message.routingkey = routingKey; message.noAck = consumer.noAck; message.propertis = properties; message.redelivered = redelivered; message.deliveryTag = deliveryTag; message.channel = consumer.channel; message.body = body; message.userData = userdata; //去处理消息 consumer.Receive?.Invoke(message); //base.HandleBasicDeliver(consumerTag, deliveryTag, redelivered, exchange, routingKey, properties, body); } public override void HandleModelShutdown(IModel model, ShutdownEventArgs reason) { base.HandleModelShutdown(model, reason); } } } } #endif