using LiveForum.IService.Messages; using LiveForum.Model.Events; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using StackExchange.Redis; using System; using System.Text.Json; using System.Threading; using System.Threading.Tasks; namespace LiveForum.WebApi.BackgroundServices { /// /// 评论回复消息后台消费者 /// 从 message:events:comment 队列消费评论回复事件 /// public class CommentReplyMessageConsumer : BackgroundService { private readonly IDatabase _database; private readonly IMessageEventHandler _eventHandler; private readonly ILogger _logger; private const string QUEUE_NAME = "message:events:comment"; public CommentReplyMessageConsumer( IDatabase database, IMessageEventHandler eventHandler, ILogger logger) { _database = database; _eventHandler = eventHandler; _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("[CommentConsumer] 评论回复消息消费者已启动"); while (!stoppingToken.IsCancellationRequested) { try { // 从Redis List左侧阻塞弹出(BLPOP,超时5秒) var result = await _database.ListLeftPopAsync(QUEUE_NAME); if (result.HasValue) { var json = result.ToString(); // 反序列化事件 var evt = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); if (evt != null) { _logger.LogInformation( "[CommentConsumer] 开始处理评论回复事件: EventId={EventId}, CommentId={CommentId}, IsDirectComment={IsDirectComment}", evt.EventId, evt.CommentId, evt.IsDirectComment); // 处理事件(带重试) await ProcessWithRetryAsync(evt, stoppingToken); } } else { // 队列为空,等待1秒再查询 await Task.Delay(1000, stoppingToken); } } catch (OperationCanceledException) { // 正常停止 break; } catch (Exception ex) { _logger.LogError(ex, "[CommentConsumer] 消费评论回复事件时发生异常"); // 发生异常后等待5秒再继续 await Task.Delay(5000, stoppingToken); } } _logger.LogInformation("[CommentConsumer] 评论回复消息消费者已停止"); } /// /// 带重试的事件处理 /// private async Task ProcessWithRetryAsync(CommentReplyEvent evt, CancellationToken stoppingToken) { const int maxRetries = 3; var retryCount = 0; while (retryCount < maxRetries && !stoppingToken.IsCancellationRequested) { try { await _eventHandler.HandleCommentReplyEventAsync(evt); _logger.LogInformation( "[CommentConsumer] 评论回复事件处理成功: EventId={EventId}", evt.EventId); return; // 成功,退出重试循环 } catch (Exception ex) { retryCount++; if (retryCount >= maxRetries) { _logger.LogError(ex, "[CommentConsumer] 评论回复事件处理失败(已重试{RetryCount}次): EventId={EventId}", retryCount, evt.EventId); // TODO: 可以将失败的事件写入死信队列或日志表 return; } _logger.LogWarning(ex, "[CommentConsumer] 评论回复事件处理失败,准备重试({RetryCount}/{MaxRetries}): EventId={EventId}", retryCount, maxRetries, evt.EventId); // 延迟后重试(指数退避) await Task.Delay(1000 * retryCount, stoppingToken); } } } public override Task StopAsync(CancellationToken cancellationToken) { _logger.LogInformation("[CommentConsumer] 正在停止评论回复消息消费者..."); return base.StopAsync(cancellationToken); } } }