Kafka 传输
发件箱 ↔ Kafka 的跨服务投递。与 RabbitMQ 传输同一套抽象(IIntegrationEventTransport),二选一。
接入
builder.Services.AddLedonOutbox();
builder.Services.AddLedonKafkaTransport(o =>
{
o.BootstrapServers = "localhost:9092";
// o.TopicPrefix = "ledon."; // 主题 = 前缀 + 事件类型全名
// o.GroupId = "orders-service"; // 默认取服务名(LEDON_SERVICE_NAME / 入口程序集名)
});
装包即两端就绪:
- 发布侧:发件箱调度器把消息投到
{TopicPrefix}{事件类型全名}主题(发送确认失败由发件箱重试); - 消费侧:自动订阅"已发现的集成事件类型"对应的主题(
SubscribeDiscoveredEventTypes,可关),反序列化后派发给进程内IIntegrationEventHandler<T>;同消费组多实例 = 竞争消费。
投递语义
- 至少一次:发布失败由发件箱重试;消费失败不提交偏移量,broker 重投;
- 恰好一次处理:配
AddLedonInbox(),broker 重投的消息按ledon-message-id头去重(见可靠投递); - 事件类型经消息头
ledon-event-type传递(程序集限定名,与OutboxMessage.Type一致)——消费方程序集须引用事件契约。
死信主题(DLQ)
处理失败超过 MaxDeliveryAttempts(默认 5 次)的消息转入 {主题}{后缀}(默认 .dlq),不再无限重投阻塞分区:
builder.Services.AddLedonKafkaTransport(o =>
{
o.BootstrapServers = "localhost:9092";
o.MaxDeliveryAttempts = 5; // 0 或负值 = 不启用死信(无限重投)
o.DeadLetterTopicSuffix = ".dlq";
});
- 死信消息保留原始 key/值与头,并附加
ledon-delivery-attempts(尝试次数)、ledon-dlq-reason(失败原因)、ledon-dlq-at(时间); - 进入死信后提交偏移量——毒消息不再阻塞分区;重置消费组偏移量可重放;
- 尝试计数是进程内的(键 = 主题/分区/偏移量),重启后重置——死信是"尽力而为"的护栏;
- 路由逻辑在
KafkaDeadLetterRouter(纯静态、无 broker 依赖),单测直接覆盖。
broker 健康检查
配了传输入口即自动登记连通性健康检查(就绪探针用,未接健康检查端点时是惰性登记):
builder.Services.AddLedonKafkaTransport(o => o.BootstrapServers = "localhost:9092");
builder.Services.AddLedonHealthChecks();
app.MapLedonReadiness(); // Kafka 挂了 → 503 摘流;/health/live 仍 200 不重启
RabbitMQ 传输(AddLedonRabbitMqTransport)同样自动登记 rabbitmq 检查。
编解码约定
编解码逻辑是独立可测的静态类(KafkaEventCodec):主题名、序列化、类型解析,单测直接覆盖,不需要 broker。
端到端测试
完整链路需要真实 broker。设置环境变量后测试自动启用(CI/本地默认跳过):
$env:LEDON_TEST_KAFKA_BOOTSTRAP = "localhost:9092"
dotnet test