第 11 章 一份可以交付的小项目
项目目标与事件契约
现在你要完成一个“订单事件观察站”:订单生产者写入 orders;通知组和统计组独立读取;能观察分区和组进度;可以演示停止、恢复和回放。它不需要真的发送短信或扣款,终端输出代表下游收到事件。
事件的约定叫契约。项目采用以下字段:
| 字段 | 含义 | 示例 |
|---|---|---|
| event_id | 一次事件的唯一标识 | e-101 |
| order_id | 业务订单号,同时作为 Key | A2001 |
| type | 已发生的事实类型 | OrderCreated |
| amount_fen | 金额,整数分 | 5800 |
| schema_version | 数据格式版本 | 1 |
真实项目还应加入明确时区的事件发生时间、追踪标识等。不要随意把身份证号、银行卡号或密钥放进事件;历史回放会放大敏感数据传播范围。
运行项目
保持第 3 章 Broker 运行,并已按第 5 章创建 orders。启动两个新组:
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 \
--topic orders --from-beginning --group project-notify \
--property print.key=true --property print.partition=truebin/kafka-console-consumer.sh --bootstrap-server localhost:9092 \
--topic orders --from-beginning --group project-stats \
--property print.key=true --property print.partition=true生产者命令与第 5 章相同,依次输入:
A2001|{"event_id":"e-101","order_id":"A2001","type":"OrderCreated","amount_fen":5800,"schema_version":1}
A2002|{"event_id":"e-102","order_id":"A2002","type":"OrderCreated","amount_fen":3900,"schema_version":1}
A2001|{"event_id":"e-103","order_id":"A2001","type":"OrderPaid","amount_fen":5800,"schema_version":1}
两个终端都应该包含这三个事件 ID。同一订单的两个事件应落入同一分区。消费者还可能显示前几章的历史数据,这是预期现象,按事件 ID 判断本次结果。
做三次故障练习
第一,暂停通知消费者,统计消费者保持运行,再发送一条新订单。统计仍能读到;通知组恢复后,应能读取暂停期间仍然保留的事件。由于停机前的提交时机不同,恢复时也可能包含重复。
第二,为通知组启动第二个实例,使用 kafka-consumer-groups.sh --describe 观察分区分配。停止其中一个后,再观察接管。
第三,停止两个通知实例,按第 7 章的方法对 project-notify 做回放。统计组不需要随之回放,因为组进度彼此独立。
项目验收表
- 能在主题描述中找到三个分区。
- 两个不同组均能看到
e-101、e-102、e-103。 - 同一个订单的创建与支付事件有相同分区编号。
- 停止一个业务组不阻止另一个业务组继续读取。
- 能解释重放为什么可能重复触发副作用。
这些命令验证了消息流动与组行为,没有实现真正的短信幂等和订单数据库事务。进阶时可选择一种熟悉的语言,把第 10 章的数据库去重流程做成小程序,再设计“业务成功后立即杀进程”的故障测试。