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