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