async Task Enqueue( List <EventMessage> Events, Action <List <string> > ackHandler, Action <List <string> > nackHandler, Action <List <string> > returnHandler, int EventDelaySeconds, int TimeoutMilliseconds, int BatchSize) { try { if (!_persistentConnection.IsConnected) { _persistentConnection.TryConnect(); } //消息发送成功后回调后需要修改数据库状态,改成本地做组缓存后,再批量入库。(性能提升百倍) var _batchBlock_BasicReturn = new BatchBlock <string>(BatchSize); var _batchBlock_BasicAcks = new BatchBlock <string>(BatchSize); var _batchBlock_BasicNacks = new BatchBlock <string>(BatchSize); var _actionBlock_BasicReturn = new ActionBlock <string[]>(EventIDs => { if (returnHandler != null && EventIDs.Length > 0) { returnHandler(EventIDs.ToList()); } }, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount }); var _actionBlock_BasicAcks = new ActionBlock <string[]>(EventIDs => { if (ackHandler != null && EventIDs.Length > 0) { ackHandler(EventIDs.ToList()); } }, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount, }); var _actionBlock_BasicNacks = new ActionBlock <string[]>(EventIDs => { if (nackHandler != null && EventIDs.Length > 0) { nackHandler(EventIDs.ToList()); } }, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount }); _batchBlock_BasicReturn.LinkTo(_actionBlock_BasicReturn); _batchBlock_BasicAcks.LinkTo(_actionBlock_BasicAcks); _batchBlock_BasicNacks.LinkTo(_actionBlock_BasicNacks); using (var _channel = _persistentConnection.CreateModel()) { //保存EventId和DeliveryTag 映射 var unconfirmEventIds = new string[Events.Count]; var returnEventIds = new Dictionary <string, bool>(); ulong lastDeliveryTag = 0; //消息无法投递失被退回(如:队列找不到) _channel.BasicReturn += async(object sender, BasicReturnEventArgs e) => { if (!string.IsNullOrEmpty(e.BasicProperties.MessageId)) { returnEventIds.Add(e.BasicProperties.MessageId, false); await _batchBlock_BasicReturn.SendAsync(e.BasicProperties.MessageId); } }; //消息路由到队列并持久化后执行 _channel.BasicAcks += async(object sender, BasicAckEventArgs e) => { if (e.Multiple) { for (var i = lastDeliveryTag; i < e.DeliveryTag; i++) { var eventId = unconfirmEventIds[i]; if (!string.IsNullOrEmpty(eventId)) { unconfirmEventIds[i] = ""; if (returnEventIds.Count > 0) { if (!returnEventIds.ContainsKey(eventId)) { await _batchBlock_BasicAcks.SendAsync(eventId); } } else { await _batchBlock_BasicAcks.SendAsync(eventId); } } } // 批量回调,记录当期位置 lastDeliveryTag = e.DeliveryTag; } else { var eventId = unconfirmEventIds[e.DeliveryTag - 1]; if (!string.IsNullOrEmpty(eventId)) { unconfirmEventIds[e.DeliveryTag - 1] = ""; if (returnEventIds.Count > 0) { if (!returnEventIds.ContainsKey(eventId)) { await _batchBlock_BasicAcks.SendAsync(eventId); } } else { await _batchBlock_BasicAcks.SendAsync(eventId); } } } }; //消息投递失败 _channel.BasicNacks += async(object sender, BasicNackEventArgs e) => { if (e.Multiple) { for (var i = lastDeliveryTag; i < e.DeliveryTag; i++) { var eventId = unconfirmEventIds[i]; if (!string.IsNullOrEmpty(eventId)) { unconfirmEventIds[i] = ""; if (returnEventIds.Count > 0) { if (!returnEventIds.ContainsKey(eventId)) { await _batchBlock_BasicNacks.SendAsync(eventId); } } else { await _batchBlock_BasicNacks.SendAsync(eventId); } } } // 批量回调,记录当期位置 lastDeliveryTag = e.DeliveryTag; } else { var eventId = unconfirmEventIds[e.DeliveryTag - 1]; if (string.IsNullOrEmpty(eventId)) { unconfirmEventIds[e.DeliveryTag - 1] = ""; if (returnEventIds.Count > 0) { if (!returnEventIds.ContainsKey(eventId)) { await _batchBlock_BasicNacks.SendAsync(eventId); } } else { await _batchBlock_BasicNacks.SendAsync(eventId); } } } }; _eventBusRetryPolicy.Execute(() => { _channel.ConfirmSelect(); }); // 提交走批量通道 var _batchPublish = _channel.CreateBasicPublishBatch(); for (var eventIndex = 0; eventIndex < Events.Count; eventIndex++) { _eventBusRetryPolicy.Execute(() => { var MessageId = Events[eventIndex].MessageId; var json = Events[eventIndex].Body; var routeKey = Events[eventIndex].RouteKey; byte[] bytes = Encoding.UTF8.GetBytes(json); //设置消息持久化 IBasicProperties properties = _channel.CreateBasicProperties(); properties.DeliveryMode = 2; properties.MessageId = MessageId; unconfirmEventIds[eventIndex] = MessageId; //需要发送延时消息 if (EventDelaySeconds > 0) { Dictionary <string, object> dic = new Dictionary <string, object>(); dic.Add("x-expires", EventDelaySeconds * 10000); //队列过期时间 dic.Add("x-message-ttl", EventDelaySeconds * 1000); //当一个消息被推送在该队列的时候 可以存在的时间 单位为ms,应小于队列过期时间 dic.Add("x-dead-letter-exchange", _exchange); //过期消息转向路由 dic.Add("x-dead-letter-routing-key", routeKey); //过期消息转向路由相匹配routingkey routeKey = routeKey + "_DELAY_" + EventDelaySeconds; //创建一个队列 _channel.QueueDeclare( queue: routeKey, durable: true, exclusive: false, autoDelete: false, arguments: dic); _batchPublish.Add( exchange: "", mandatory: true, routingKey: routeKey, properties: properties, body: bytes); } else { _batchPublish.Add( exchange: _exchange, mandatory: true, routingKey: routeKey, properties: properties, body: bytes); } }); } ; await _eventBusRetryPolicy.Execute(async() => { await Task.Run(() => { //批量提交 _batchPublish.Publish(); _channel.WaitForConfirms(TimeSpan.FromMilliseconds(TimeoutMilliseconds)); }); }); } _batchBlock_BasicAcks.Complete(); _batchBlock_BasicNacks.Complete(); _batchBlock_BasicReturn.Complete(); await _batchBlock_BasicReturn.Completion.ContinueWith(delegate { _actionBlock_BasicReturn.Complete(); }); await _batchBlock_BasicAcks.Completion.ContinueWith(delegate { _actionBlock_BasicAcks.Complete(); }); await _batchBlock_BasicNacks.Completion.ContinueWith(delegate { _actionBlock_BasicNacks.Complete(); }); } catch (Exception ex) { _logger.LogError(ex, ex.Message); } }
/// <summary> /// 发送消息 /// </summary> async Task Enqueue( Dictionary <string, Dictionary <string, string> > Events, Action <List <string> > ackHandler = null, Action <List <string> > nackHandler = null, Action <List <string> > returnHandler = null, int EventDelaySeconds = 0, int TimeoutMilliseconds = 500, int BatchSize = 500) { try { if (!_persistentConnection.IsConnected) { _persistentConnection.TryConnect(); } var policy = RetryPolicy.Handle <BrokerUnreachableException>() .Or <SocketException>() .Or <System.IO.IOException>() .Or <AlreadyClosedException>() .WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) => { _logger.LogWarning(ex.ToString()); }); //消息发送成功后回调后需要修改数据库状态,改成本地做组缓存后,再批量入库。(性能提升百倍) var _batchBlock_BasicReturn = new BatchBlock <string>(BatchSize); var _batchBlock_BasicAcks = new BatchBlock <string>(BatchSize); var _batchBlock_BasicNacks = new BatchBlock <string>(BatchSize); var _actionBlock_BasicReturn = new ActionBlock <string[]>(EventIDs => { if (returnHandler != null && EventIDs.Length > 0) { returnHandler(EventIDs.ToList()); } }); var _actionBlock_BasicAcks = new ActionBlock <string[]>(EventIDs => { if (ackHandler != null && EventIDs.Length > 0) { ackHandler(EventIDs.ToList()); } }); var _actionBlock_BasicNacks = new ActionBlock <string[]>(EventIDs => { if (nackHandler != null && EventIDs.Length > 0) { nackHandler(EventIDs.ToList()); } }); _batchBlock_BasicReturn.LinkTo(_actionBlock_BasicReturn); _batchBlock_BasicAcks.LinkTo(_actionBlock_BasicAcks); _batchBlock_BasicNacks.LinkTo(_actionBlock_BasicNacks); _batchBlock_BasicReturn.Completion.ContinueWith(delegate { _actionBlock_BasicReturn.Complete(); }); _batchBlock_BasicAcks.Completion.ContinueWith(delegate { _actionBlock_BasicAcks.Complete(); }); _batchBlock_BasicNacks.Completion.ContinueWith(delegate { _actionBlock_BasicNacks.Complete(); }); using (var _channel = _persistentConnection.CreateModel()) { //保存EventId和DeliveryTag 映射 var deliveryTags = new Dictionary <ulong, string>(); var returnEventIds = new System.Collections.Hashtable(); //消息无法投递失被退回(如:队列找不到) _channel.BasicReturn += (object sender, BasicReturnEventArgs e) => { if (!string.IsNullOrEmpty(e.BasicProperties.MessageId)) { _batchBlock_BasicReturn.Post(e.BasicProperties.MessageId); returnEventIds.Add(e.BasicProperties.MessageId, false); } }; //消息路由到队列并持久化后执行 _channel.BasicAcks += (object sender, BasicAckEventArgs e) => { var EventIDs = new List <string>(); if (e.Multiple) { foreach (var EventID in deliveryTags.Where(a => a.Key < e.DeliveryTag + 1).Select(a => a.Value)) { if (!EventIDs.Contains(EventID) && !returnEventIds.ContainsKey(EventID)) { EventIDs.Add(EventID); } } } else { var EventID = deliveryTags[e.DeliveryTag]; if (!EventIDs.Contains(EventID) && !returnEventIds.ContainsKey(EventID)) { EventIDs.Add(deliveryTags[e.DeliveryTag]); } } EventIDs.ForEach(eventId => { _batchBlock_BasicAcks.Post(eventId); }); }; //消息投递失败 _channel.BasicNacks += (object sender, BasicNackEventArgs e) => { var EventIDs = new List <string>(); //批量确认 if (e.Multiple) { foreach (var EventID in deliveryTags.Where(a => a.Key < e.DeliveryTag + 1).Select(a => a.Value)) { if (!EventIDs.Contains(EventID)) { EventIDs.Add(EventID); } } } else { var EventID = deliveryTags[e.DeliveryTag]; if (!EventIDs.Contains(EventID)) { EventIDs.Add(EventID); } } EventIDs.ForEach(eventId => { _batchBlock_BasicNacks.Post(eventId); }); }; policy.Execute(() => { _channel.ConfirmSelect(); }); foreach (var msg in Events) { policy.Execute(() => { var EventId = msg.Key; var json = msg.Value["Body"]; var routeKey = msg.Value["EventTypeName"]; byte[] bytes = Encoding.UTF8.GetBytes(json); //设置消息持久化 IBasicProperties properties = _channel.CreateBasicProperties(); properties.DeliveryMode = 2; properties.MessageId = msg.Key; if (!deliveryTags.ContainsValue(EventId)) { deliveryTags.Add((ulong)deliveryTags.Count + 1, EventId); } //需要发送延时消息 if (EventDelaySeconds > 0) { Dictionary <string, object> dic = new Dictionary <string, object>(); dic.Add("x-expires", EventDelaySeconds * 10000); //队列过期时间 dic.Add("x-message-ttl", EventDelaySeconds * 1000); //当一个消息被推送在该队列的时候 可以存在的时间 单位为ms,应小于队列过期时间 dic.Add("x-dead-letter-exchange", _exchange); //过期消息转向路由 dic.Add("x-dead-letter-routing-key", routeKey); //过期消息转向路由相匹配routingkey routeKey = routeKey + "_DELAY_" + EventDelaySeconds; //创建一个队列 _channel.QueueDeclare( queue: routeKey, durable: true, exclusive: false, autoDelete: false, arguments: dic); _channel.BasicPublish( exchange: "", mandatory: true, routingKey: routeKey, basicProperties: properties, body: bytes); } else { _channel.BasicPublish( exchange: _exchange, mandatory: true, routingKey: routeKey, basicProperties: properties, body: bytes); } }); } policy.Execute(() => { _channel.WaitForConfirms(TimeSpan.FromSeconds(TimeoutMilliseconds)); }); } _batchBlock_BasicAcks.Complete(); _batchBlock_BasicNacks.Complete(); _batchBlock_BasicReturn.Complete(); _actionBlock_BasicNacks.Completion.Wait(); _actionBlock_BasicAcks.Completion.Wait(); _actionBlock_BasicReturn.Completion.Wait(); } catch (Exception ex) { _logger.LogError(ex, ex.Message); } }