live-forum/server/webapi/LiveForum/LiveForum.WebApi/BackgroundServices/CustomMessageConsumer.cs
2026-03-24 11:27:37 +08:00

140 lines
5.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
{
/// <summary>
/// 自定义消息后台消费者
/// 从 message:events:custom 队列消费自定义消息事件
/// </summary>
public class CustomMessageConsumer : BackgroundService
{
private readonly IDatabase _database;
private readonly IMessageEventHandler _eventHandler;
private readonly ILogger<CustomMessageConsumer> _logger;
private const string QUEUE_NAME = "message:events:custom";
public CustomMessageConsumer(
IDatabase database,
IMessageEventHandler eventHandler,
ILogger<CustomMessageConsumer> logger)
{
_database = database;
_eventHandler = eventHandler;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("[CustomConsumer] 自定义消息消费者已启动");
while (!stoppingToken.IsCancellationRequested)
{
try
{
// 从Redis List左侧弹出
var result = await _database.ListLeftPopAsync(QUEUE_NAME);
if (result.HasValue)
{
var json = result.ToString();
// 反序列化事件
var evt = JsonSerializer.Deserialize<CustomMessageEvent>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
if (evt != null)
{
_logger.LogInformation(
"[CustomConsumer] 开始处理自定义消息事件: EventId={EventId}, MessageType={MessageType}",
evt.EventId, evt.MessageType);
// 处理事件(带重试)
await ProcessWithRetryAsync(evt, stoppingToken);
}
}
else
{
// 队列为空等待1秒再查询
await Task.Delay(1000, stoppingToken);
}
}
catch (OperationCanceledException)
{
// 正常停止
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "[CustomConsumer] 消费自定义消息事件时发生异常");
// 发生异常后等待5秒再继续
await Task.Delay(5000, stoppingToken);
}
}
_logger.LogInformation("[CustomConsumer] 自定义消息消费者已停止");
}
/// <summary>
/// 带重试的事件处理
/// </summary>
private async Task ProcessWithRetryAsync(CustomMessageEvent evt, CancellationToken stoppingToken)
{
const int maxRetries = 3;
var retryCount = 0;
while (retryCount < maxRetries && !stoppingToken.IsCancellationRequested)
{
try
{
await _eventHandler.HandleCustomMessageEventAsync(evt);
_logger.LogInformation(
"[CustomConsumer] 自定义消息事件处理成功: EventId={EventId}",
evt.EventId);
return; // 成功,退出重试循环
}
catch (Exception ex)
{
retryCount++;
if (retryCount >= maxRetries)
{
_logger.LogError(ex,
"[CustomConsumer] 自定义消息事件处理失败(已重试{RetryCount}次): EventId={EventId}",
retryCount, evt.EventId);
// TODO: 可以将失败的事件写入死信队列或日志表
return;
}
_logger.LogWarning(ex,
"[CustomConsumer] 自定义消息事件处理失败,准备重试({RetryCount}/{MaxRetries}): EventId={EventId}",
retryCount, maxRetries, evt.EventId);
// 延迟后重试(指数退避)
await Task.Delay(1000 * retryCount, stoppingToken);
}
}
}
public override Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("[CustomConsumer] 正在停止自定义消息消费者...");
return base.StopAsync(cancellationToken);
}
}
}