Beispiel #1
0
        public void Restart()
        {
            Cancel();

            try
            {
                _channel = CreateChannel(_connectionSetting.BrokerName, false);

                string queueName = EventTypeToRabbitQueueName(_eventType);

                _channel.QueueDeclarePassive(queueName);

                _channel.BasicQos(0, _connectionSetting.ConsumePerTimeNumber, false);

                EventingBasicConsumer consumer = new EventingBasicConsumer(_channel);

                consumer.Received += (sender, eventArgs) =>
                {
                    EventMessageEntity entity = JsonUtil.DeSerialize <EventMessageEntity>(eventArgs.Body);

                    //时间戳检测
                    if (CheckTimestamp(entity))
                    {
                        //防重检测
                        //TODO: 不是太重要的话,可以考虑放到内存中来
                        bool setted = _redis.KeySetIfNotExist(_connectionSetting.RedisInstanceName, entity.Id, expireSeconds: _connectionSetting.AliveSeconds);

                        if (setted)
                        {
                            _handler.Handle(entity.JsonData);
                        }
                        else
                        {
                            _logger.LogWarning($"找到一个重复EventMessage, Type : {entity.Type}, Timestamp:{entity.Timestamp}, Data:{entity.JsonData}");
                        }
                    }
                    else
                    {
                        _logger.LogWarning($"找到一个过期EventMessage, Type : {entity.Type}, Timestamp:{entity.Timestamp}, Data:{entity.JsonData}");
                    }

                    _channel.BasicAck(eventArgs.DeliveryTag, false);
                };

                _consumeTag = _channel.BasicConsume(queueName, false, consumer);
            }
            catch (Exception ex)
            {
                _logger.LogCritical(ex, $"在Consume RabbitMQ {_connectionSetting.BrokerName} 中,Exceptions: {ex.Message}");
            }
            finally
            {
                Cancel();

                if (AutoRecovery)
                {
                    Restart();
                }
            }
        }
Beispiel #2
0
        private bool CheckTimestamp(EventMessageEntity entity)
        {
            long seconds = TimeUtil.CurrentTimestampSeconds() - entity.Timestamp;

            if (seconds <= _connectionSetting.AliveSeconds)
            {
                return(true);
            }

            _logger.LogCritical($"找到一个过期的EventMessage, Type : {entity.Type}, Timestamp:{entity.Timestamp}, Data:{entity.JsonData}");

            return(false);
        }
        public async Task <bool> PublishAsync(string brokerName, EventMessage eventMessage)
        {
            //大量Request线程放入缓存池中,离开
            //缓存池内容不能丢,所以用抗击打的Redis来存储
            //注意取消息后需要从kvstore删除

            if (!IsBrokerExists(brokerName))
            {
                throw new Exception($"Not exist rabbit broker:{brokerName}");
            }

            EventMessageEntity        eventEntity       = new EventMessageEntity(eventMessage.Type, eventMessage.JsonData);
            RabbitMQConnectionSetting connectionSetting = _options.GetConnectionSetting(brokerName);

            //推送到一个以broker命名的队列中
            await _redis.PushAsync(redisInstanceName : connectionSetting.RedisInstanceName, queueName : brokerName, data : eventEntity);

            //NotifyPublishToRabbitMQ(brokerName);

            return(true);
        }
        private void DeclareRabbitMQ(IModel channel, EventMessageEntity message)
        {
            if (message == null)
            {
                return;
            }

            if (_eventDeclareDict.ContainsKey(message.Type))
            {
                return;
            }

            string queueName  = EventTypeToRabbitQueueName(message.Type);
            string routingKey = EventTypeToRabbitRoutingKey(message.Type);

            //Queue
            channel.QueueDeclare(queueName, true, false, false);

            //Bind
            channel.QueueBind(queueName, _connectionSetting.ExchangeName, routingKey);

            _eventDeclareDict.TryAdd(message.Type, true);
        }
        protected override void TaskProcedure()
        {
            //per thread per channel . await前后线程不同。想让前后线程一致。Task里避免用await,用wait()

            _logger.LogTrace($"PublishToRabbitMQ Task Start, ThreadID:{Thread.CurrentThread.ManagedThreadId}");

            IModel channel = null;

            List <string> confirmEventIdList = new List <string>();

            ulong fs = 1; //the seqno of first in confirmEventIdList

            object confirmEventIdListLocker = new object();

            try
            {
                channel = CreateChannel(brokerName: _connectionSetting.BrokerName, isPublish: true);

                channel.BasicAcks += (sender, eventArgs) =>
                {
                    _logger.LogTrace($"Ack: {eventArgs.DeliveryTag}, Multiple:{eventArgs.Multiple}");

                    List <string> deleteIds = new List <string>();

                    lock (confirmEventIdListLocker)
                    {
                        ulong seqno = eventArgs.DeliveryTag;
                        int   index = (int)(seqno - fs);

                        if (index >= 0 && index < confirmEventIdList.Count)
                        {
                            if (eventArgs.Multiple)
                            {
                                for (int i = 0; i < index; i++)
                                {
                                    if (confirmEventIdList[i] != null)
                                    {
                                        deleteIds.Add(confirmEventIdList[i]);
                                        confirmEventIdList[i] = null;
                                    }
                                }
                            }
                            else
                            {
                                deleteIds.Add(confirmEventIdList[index]);
                                confirmEventIdList[index] = null;
                            }
                        }

                        // 收缩,每100次,收缩一次
                        if (seqno % 100 == 0 && confirmEventIdList.Count > 100)
                        {
                            _logger.LogTrace($"开始收缩:TheadID:{Thread.CurrentThread.ManagedThreadId}, count:{confirmEventIdList.Count}, fs:{fs}");

                            //奇点处理,直接当成nack
                            confirmEventIdList[0] = null;

                            int nextIndex = 0;

                            for (int i = 0; i < confirmEventIdList.Count; ++i)
                            {
                                if (confirmEventIdList[i] != null)
                                {
                                    nextIndex = i;
                                    break;
                                }
                            }

                            fs += (ulong)nextIndex;

                            confirmEventIdList.RemoveRange(0, nextIndex);
                            confirmEventIdList.TrimExcess();
                        }
                    }

                    //将deleteIds放入 DistributedConfirmEventIdSet

                    List <int> valueList = new List <int>();

                    foreach (string id in deleteIds)
                    {
                        valueList.Add(1);
                    }

                    _redis.HashSetInt(redisInstanceName: _connectionSetting.RedisInstanceName, hashName: DistributedConfirmIdHashName, fields: deleteIds, values: valueList);
                };

                channel.BasicNacks += (sender, eventArgs) =>
                {
                    _logger.LogWarning($"NooooAck: {eventArgs.DeliveryTag}, Multiple:{eventArgs.Multiple}");

                    //List<string> deleteIds = new List<string>();

                    ////那就在history里待着吧,等待回收
                    lock (confirmEventIdListLocker)
                    {
                        ulong seqno = eventArgs.DeliveryTag;
                        int   index = (int)(seqno - fs);

                        if (eventArgs.Multiple)
                        {
                            for (int i = 0; i < index; i++)
                            {
                                if (confirmEventIdList[i] != null)
                                {
                                    //deleteIds.Add(confirmEventIdList[i]);
                                    confirmEventIdList[i] = null;
                                }
                            }
                        }
                        else
                        {
                            //deleteIds.Add(confirmEventIdList[index]);
                            confirmEventIdList[index] = null;
                        }
                    }

                    //将deleteIds放入 DistributedConfirmEventIdSet

                    //List<int> valueList = new List<int>();

                    //foreach (string id in deleteIds)
                    //{
                    //    valueList.Add(0);
                    //}

                    //_redis.HashSetInt(redisInstanceName: _connectionSetting.RedisInstanceName, hashName: DistributedConfirmIdHashName, fields: deleteIds, values: valueList);
                };

                while (true)
                {
                    //获取数据.用同步方法,不能用await,避免前后线程不一致

                    EventMessageEntity eventEntity = _redis.PopAndPush <EventMessageEntity>(
                        redisInstanceName: _connectionSetting.RedisInstanceName,
                        fromQueueName: DistributedQueueName,
                        toQueueName: DistributedHistoryQueueName
                        );

                    //没有数据,queue为空,直接推出,结束Thread
                    if (eventEntity == null)
                    {
                        bool allAcks = channel.WaitForConfirms(TimeSpan.FromSeconds(_connectionSetting.MaxSecondsWaitForConfirms), out bool timedOut);

                        if (allAcks && !timedOut)
                        {
                            _logger.LogDebug($"Task will end, no in queue : {DistributedQueueName}, and all good.");
                        }
                        else if (allAcks && timedOut)
                        {
                            _logger.LogWarning($"Task will end, WaitForConfirms timedOut, with broker : {_connectionSetting.BrokerName}, and senconds : {_connectionSetting.MaxSecondsWaitForConfirms}");
                        }
                        else
                        {
                            _logger.LogError($"Task will end, and there have nacks, with broker : {_connectionSetting.BrokerName}");
                        }

                        break;
                    }

                    //Channel 不可用,直接结束Thread
                    if (channel == null || channel.CloseReason != null)
                    {
                        _logger.LogWarning($"Channel for broker: {_connectionSetting.BrokerName}, has closed, reason:{channel?.CloseReason.ToString()}");

                        break;
                    }

                    //Declare Queue & Binding
                    DeclareRabbitMQ(channel, eventEntity);

                    //publish
                    IBasicProperties basicProperties = channel.CreateBasicProperties();
                    basicProperties.DeliveryMode = 2;

                    channel.BasicPublish(
                        _connectionSetting.ExchangeName,
                        EventTypeToRabbitRoutingKey(eventEntity.Type),
                        true,
                        basicProperties,
                        JsonUtil.Serialize(eventEntity)
                        );

                    //_logger.LogTrace($"event published. Type:{eventEntity.Type}, Data:{eventEntity.JsonData}, Id:{eventEntity.Id}, Timestamp:{eventEntity.Timestamp}");

                    //Confirm
                    confirmEventIdList.Add(eventEntity.Id);
                }
            }
            catch (Exception ex)
            {
                _logger.LogCritical(ex, $"在PublishToRabbitMQ {_connectionSetting.BrokerName} 中,Thread Id : {Thread.CurrentThread.ManagedThreadId}, Exceptions: {ex.Message}");
            }
            finally
            {
                bool?allAcks = channel?.WaitForConfirms(TimeSpan.FromSeconds(_connectionSetting.MaxSecondsWaitForConfirms));

                _logger.LogTrace($"Tread ID {Thread.CurrentThread.ManagedThreadId} de channel waiting end. begin to close. allAkcs? {allAcks}");

                channel?.Close();

                OnTaskFinished();
            }
        }