文件内容 ChannelHelper.cs 10 KB 10568 字符
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using PasteApeart.Application.Contracts;
using PasteApeart.Handler.Extend;
using PasteApeart.Handler.Extensions;
using PasteApeart.Handler.rabbit;
using PasteApeart.Handler.redis;
using System.Collections.Concurrent;
using System.Text.Json;
using System.Threading.Channels;

namespace PasteApeart.Handler
{
    /// <summary>
    /// 
    /// </summary>
    public class ChannelHelper : IChannelHelper
    {
        private readonly Channel<IEventModel> _channelLocationQueue;
        private readonly ILogger<ChannelHelper> _logger;
        private readonly ConcurrentDictionary<long, List<IEventModel>> queueList;
        private readonly IRabbitMQProducer _producer;
        private readonly RabbitConfig _rabbitConfig;
        private readonly IAppCache _appCache;

        /// <summary>
        /// 
        /// </summary>
        public ChannelHelper(ILogger<ChannelHelper> logger, IRabbitMQProducer producer, IAppCache appCache, IOptions<RabbitConfig> rabbitConfig)
        {
            queueList = new ConcurrentDictionary<long, List<IEventModel>>();
            _logger = logger;
            _rabbitConfig = rabbitConfig.Value;
            if (_rabbitConfig.ChannelCapacity <= 0)
            {
                _channelLocationQueue = Channel.CreateUnbounded<IEventModel>();
            }
            else
            {
                _channelLocationQueue = Channel.CreateBounded<IEventModel>(_rabbitConfig.ChannelCapacity);
            }
            _appCache = appCache;
            if (!_rabbitConfig.RabbitQueue)
            {
                _ = PeriodicTimerHandlerAsync(default);
            }
            else
            {
                _producer = producer;
            }
        }

        /// <summary>
        /// 自动写入最近的延迟队列 本地或者远端
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task WriteAutoQueueAsync(IEventModel input)
        {
            if (String.IsNullOrEmpty(input.EventName))
            {
                input.EventName = input.GetType().Name;
            }

            if (input is AbsEventModel abModel)
            {
                if (!string.IsNullOrEmpty(abModel.MutexKey))
                {
                    if (string.IsNullOrEmpty(abModel.MutexVal))
                    {
                        await abModel.ToAutoStartTaskMutexAsync(_appCache, null, (int)(abModel.ExpireTime + 10));
                    }
                }
                if (abModel.IsSubscribe)
                {
                    if (_rabbitConfig.SubscribeEnable)
                    {
                        await _producer.WriteSubscribeAsync(System.Text.Json.JsonSerializer.Serialize(input, input.GetType(), JsonConfig.Options));
                    }
                    else
                    {
                        _logger.LogError("没有配置广播模式,当前消息是发给广播模式的");
                    }
                    return;
                }
            }

            if (input.ExpireSecond <= 0)
            {
                await SendQueueMessageAsync(input);
            }
            else
            {
                if (_rabbitConfig.RabbitQueue)
                {
                    var send = false;
                    if (_rabbitConfig.ExpireTimeList != null)
                    {
                        var find = _rabbitConfig.ExpireTimeList.Where(x => x.ExpireSecond <= input.ExpireSecond).OrderByDescending(x => x.ExpireSecond).FirstOrDefault();
                        if (find != null)
                        {
                            input.ExpireSecond = input.ExpireSecond - find.ExpireSecond;
                            await this.SendRemoteMessageAsync(input, find.ExpireExchange);
                            send = true;
                        }
                    }
                    if (!send)
                    {
                        input.ExpireSecond = 0;
                        await this.SendQueueMessageAsync(input);
                    }
                }
                else
                {
                    await this.WriteLocationMessageAsync(input, input.ExpireSecond);
                }
            }
        }


        /// <summary>
        /// 需要设定MutexKey的时候调用
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <returns></returns>
        [Obsolete("use WriteAutoQueueAsync before new MutexKey")]
        public async Task WriteMutexMQAsync<T>(T input) where T : IEventModel
        {
            if (input is AbsEventModel abs)
            {
                await abs.ToStartTaskMutexAsync(_appCache, null, 5);
            }
            await WriteAutoQueueAsync(input);
        }

        /// <summary>
        /// 需要设定MutexKey的时候调用
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        [Obsolete("use WriteAutoQueueAsync before new MutexKey")]
        public async Task WriteMutexMQAsync<T>(T input, object id) where T : IEventModel
        {
            if (input is AbsEventModel abs)
            {
                await abs.ToStartTaskMutexAsync(_appCache, id, 5);
            }
            await WriteAutoQueueAsync(input);
        }

        /// <summary>
        /// 处理本地延迟队列
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task PeriodicTimerHandlerAsync(CancellationToken cancellationToken = default)
        {
            try
            {
                TimeSpan inter = TimeSpan.FromSeconds(1);
                using var timer = new PeriodicTimer(inter);
                while (await timer.WaitForNextTickAsync(cancellationToken))
                {
                    //每隔1秒钟这里会一直重复执行么? 不考虑执行任务耗时
                    #region
                    var now = DateTime.Now.ToUnixTimeSeconds();
                    var finds = queueList.Keys.Where(x => x < now).ToArray();
                    if (finds?.Length > 0)
                    {
                        var findValues = queueList.Where(x => finds.Contains(x.Key)).ToArray();
                        if (findValues?.Length > 0)
                        {
                            foreach (var item in findValues)
                            {
                                if (item.Value != null)
                                {
                                    foreach (var ival in item.Value)
                                    {
                                        await WriteLocationMessageAsync(ival);
                                    }
                                }
                            }
                        }
                        foreach (var item in findValues)
                        {
                            queueList.TryRemove(item.Key, out _);
                        }
                    }
                    //查找是否有过期的任务,进行执行!
                    #endregion
                }
            }
            catch (Exception exlAll)
            {
                _logger.LogException(exlAll);
            }
        }

        /// <summary>
        /// 写入本地 延迟队列
        /// </summary>
        /// <param name="input"></param>
        /// <param name="second"></param>
        /// <returns></returns>
        private async Task WriteLocationMessageAsync(IEventModel input, long second)
        {
            if (input.ExpireSecond > 0)
            {
                input.ExpireSecond = 0;
            }
            if (second > 0)
            {
                var distionTime = DateTime.Now.ToUnixTimeSeconds() + second;
                queueList.AddOrUpdate(distionTime, (a) => new List<IEventModel> { input }
                , (c, d) =>
                {
                    d.Add(input);
                    return d;
                });
            }
            else
            {
                await WriteLocationMessageAsync(input);
            }
        }

        /// <summary>
        /// 直接发送本地
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        private async Task WriteLocationMessageAsync(IEventModel input)
        {
            //_logger.LogInformation($"本地MQ写入:{JsonSerializer.Serialize(input, input.GetType(), JsonConfig.Options)}");
            await _channelLocationQueue.Writer.WriteAsync(input);
        }

        /// <summary>
        /// 写入队列 本地或者远程
        /// </summary>
        /// <param name="_info"></param>
        /// <returns></returns>
        private async Task SendQueueMessageAsync(IEventModel _info)
        {
            if (_rabbitConfig.RabbitQueue)
            {
                await _producer.SendWithRetryAsync(_rabbitConfig.ExpiredExchange, JsonSerializer.Serialize(_info, _info.GetType(), JsonConfig.Options), _rabbitConfig.ExpireRouteKey);
            }
            else
            {
                //await _channelLocationQueue.Writer.WriteAsync(_info);
                await WriteLocationMessageAsync(_info);
            }
        }

        /// <summary>
        /// 直接发送远端
        /// </summary>
        /// <param name="info"></param>
        /// <param name="exchange"></param>
        /// <returns></returns>
        private async Task SendRemoteMessageAsync(IEventModel info, string exchange)
        {
            await _producer.SendWithRetryAsync(exchange, JsonSerializer.Serialize(info, info.GetType(), JsonConfig.Options), _rabbitConfig.ExpireRouteKey);
        }

        /// <summary>
        /// 写入广播事件
        /// </summary>
        /// <param name="input"></param>
        /// <param name="isremove"></param>
        /// <returns></returns>
        [Obsolete("use WriteAutoQueueAsync before set IsSubscribe = true")]
        public async Task WriteBroadcastMessageAsync(IEventModel input, bool isremove)
        {
            if (input == null) { return; }
            input.EventName = input.GetType().Name;
            if (_rabbitConfig.SubscribeEnable)
            {
                await _producer.WriteSubscribeAsync(System.Text.Json.JsonSerializer.Serialize(input, input.GetType(), JsonConfig.Options));
            }
            else
            {
                _logger.LogError("没有配置广播模式,当前消息是发给广播模式的");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public Channel<IEventModel> ChannelEventModel { get { return _channelLocationQueue; } }

    }

}
​ ​