Esempio n. 1
0
        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);
            }
        }
Esempio n. 2
0
        /// <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);
            }
        }