Event.Subscribe - dotnet-shashlik/shashlik.eventbus GitHub Wiki

事件订阅

IEventHandler

事件订阅使用接口 IEventHandler<TEvent>,泛型参数即为订阅的事件类型。事件处理类将自动注册到 DI 容器,无需手动注册。处理器不关心事件是否为延迟事件,延迟由发布方决定。

public interface IEventHandler<in TEvent> where TEvent : IEvent
{
    Task Execute(TEvent @event, IDictionary<string, string> items);
}

参数说明:

  • TEvent @event:事件实例
  • IDictionary<string, string> items:附加数据,EventBus 已自动添加以下 key:
    • eventbus-msg-id:消息 ID
    • eventbus-send-at:发送时间
    • eventbus-delay-at:延迟执行时间
    • eventbus-event-name:事件名称

示例

// 一个事件可以有多个处理类,可以分布在不同的微服务中
// 但类名不可重复,默认类名会定义为消息队列中的 queue/group

// 发送短信的处理类
public class NewUserEventForSmsHandler : IEventHandler<NewUserEvent>
{
    public async Task Execute(NewUserEvent @event, IDictionary<string, string> items)
    {
        // 发送短信...
    }
}

// 发放消费券的处理类
public class NewUserEventForCouponsHandler : IEventHandler<NewUserEvent>
{
    public async Task Execute(NewUserEvent @event, IDictionary<string, string> items)
    {
        // 业务处理...
    }
}

// 延迟活动推送的处理类
public class NewUserPromotionEventHandler : IEventHandler<NewUserPromotionEvent>
{
    public async Task Execute(NewUserPromotionEvent @event, IDictionary<string, string> items)
    {
        // 业务处理...
    }
}

EventBusNameAttribute

事件处理器名称默认规则为 {Type.Name}.{Environment},对应消息队列中的 queue/groupId。可通过 [EventBusName] 特性显式声明:

[EventBusName("order.send-sms")]
public class OrderCreatedSmsHandler : IEventHandler<OrderCreatedEvent>
{
    // ...
}

以 RabbitMQ + Production 环境为例:

  • [EventBusName("order.send-sms")] → 绑定到队列 order.send-sms.Production
  • 无特性 → 绑定到队列 OrderCreatedSmsHandler.Production

处理器 DI 生命周期

处理器通过 EventBusOptions.HandlerServiceLifetime 配置注册到 DI 容器的生命周期,默认为 Transient。每个处理器在独立的 IServiceScope 中创建实例,支持 scoped 依赖注入。

关于消息幂等

EventBus 不能保证消息的幂等性。 为了保证消息的可靠传输,EventBus 及消息中间件对消息 QOS 处理等级必须为 at least once(至少到达一次),一般消息中间件都需要开启消息持久化避免消息丢失。

一个事件处理类可能处理多次同一个事件,消息的幂等性必须由业务方处理。例如:

  • 用户订单付款完成后修改订单状态为待发货,可能在处理器中收到多次同一订单的付款完成事件
  • 幂等处理:在处理器中使用锁或判断订单状态,如果订单状态已经为待发货,则直接返回并忽略本次事件
⚠️ **GitHub.com Fallback** ⚠️