ABP vNext 的 Outbox/Inbox 系统化落地(非 CAP / 非 Kafka):幂等写、去重、延迟到达与死信回补

ABP vNext 的 Outbox/Inbox 系统化落地(非 CAP / 非 Kafka):幂等写、去重、延迟到达与死信回补 ✨



一、问题与目标 🧭

不使用 DTC;以本地事务 + Outbox 实现“效果上的一次”。核心要解决:重复投递延迟/乱序幂等更新不可恢复失败(死信)。💡


二、总体架构 🏗️

消费者(读路径)
消息中间件
Outbox Dispatcher(后台服务)
数据库
应用层(写路径)
claim→提交
Inbox 占位去重
业务处理(幂等 Upsert)
Exchange/Topic
发布到 Broker
回写状态/重试/死信
(Outbox 表)
Inbox 表
聚合/领域事件
AppService
  • 写路径:AppService → 聚合操作 → SaveChanges() 同事务 写业务表 & Outbox 表;提交后由 Dispatcher 异步派发到 Broker。
  • 读路径:Consumer 收到消息 → Inbox 先占位去重(messageId, consumer))→ 幂等处理 → 提交。
  • 可观测性:记录 commit→publish、publish→consume 延迟与重试/死信指标。🔍

三、数据模型与表结构 🗃️

兼容多租户、多版本与回补;补齐运营定位字段。✅

OutboxMessages(PostgreSQL 版)

CREATE TABLE outbox_messages (
  id               uuid PRIMARY KEY,
  aggregate_id     uuid        NOT NULL,
  aggregate_type   text        NOT NULL,
  aggregate_version bigint     NOT NULL DEFAULT 0,  -- 乱序/并发合并
  type             text        NOT NULL,            -- 事件类型/路由键
  payload          jsonb       NOT NULL,
  headers          jsonb       NOT NULL,
  tenant_id        text,
  occurred_at      timestamptz NOT NULL,
  visible_at       timestamptz NOT NULL,            -- 延迟/重试
  attempts         int         NOT NULL DEFAULT 0,
  status           smallint    NOT NULL DEFAULT 0,  -- 0:new 1:sent 3:dead 9:processing
  last_error       text,
  routing_key      text,
  partition_key    text
);

CREATE INDEX idx_outbox_visibility ON outbox_messages(status, visible_at);
CREATE INDEX idx_outbox_tenant     ON outbox_messages(tenant_id);
CREATE INDEX idx_outbox_type       ON outbox_messages(type);

SQL Server:把 jsonbnvarchar(max),或加 JSON 校验约束;时间函数换 SYSUTCDATETIME()

Inbox(去重/幂等)

CREATE TABLE inbox (
  message_id   uuid        NOT NULL,
  consumer     text        NOT NULL,
  processed_at timestamptz NOT NULL,
  tenant_id    text,
  PRIMARY KEY (message_id, consumer)
);

(message_id, consumer) 作为幂等键;支持 TTL/归档。🧹


四、ABP 集成点 🧩

使用自研事件收集接口,避免与 ABP 内置 Outbox 混用。聚合根实现 IHasDomainEvents,拦截器在同事务落 Outbox。

4.1 领域事件接口与基类

public interface IDomainEvent
{
   
   
    Guid AggregateId {
   
    get; }
    string AggregateType {
   
    get; }
    long AggregateVersion {
   
    get; }
    string TenantId {
   
    get; }
}

public interface IHasDomainEvents
{
   
   
    IReadOnlyCollection<IDomainEvent> DomainEvents {
   
    get; }
    void ClearDomainEvents();
}

public abstract class AggregateRootBase : Entity<Guid>, IHasDomainEvents
{
   
   
    private readonly List<IDomainEvent> _domainEvents = new();
    public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
    protected void RaiseDomainEvent(IDomainEvent @event) => _domainEvents.Add(@event);
    public void ClearDomainEvents() => _domainEvents.Clear();
}

4.2 Outbox 实体与工厂

public enum OutboxStatus {
   
    New = 0, Sent = 1, Dead = 3, Processing = 9 }

public sealed class OutboxMessage
{
   
   
    public Guid Id {
   
    get; init; }
    public Guid AggregateId {
   
    get; init
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kookoos

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值