实现outbox pattern的通常方法是将邮件有效负载存储在发件箱表中,并有一个单独的过程(邮件中继)查询待处理邮件,并将其发布到邮件经纪人,以我为例,卡夫卡。
发件箱表的状态可能如下所示。
OUTBOX TABLE
---------------------------------
|ID | STATE | TOPIC | PAYLOAD |
---------------------------------
| 1 | PROCESSED | user |
| 2 | PENDING | user |
| 3 | PENDING | billing |
----------------------------------
我的消息中继是一个Spring Boot / Cloud Stream应用程序,它定期(@Scheduled
)查找PENDING记录,将其发布到Kafka并将记录更新为PROCESSED状态。
第一个问题:如果我启动消息中继的多个实例,所有这些实例都将查询发件箱表,并且可能在某些时候,不同的实例将获得相同的PENDING注册中心以发布到Kafka中,生成重复的消息。我该如何预防?
另一种情况:假设仅一个消息中继。它获得一个PENDING记录,将其发布到主题,但是在将记录更新为PROCESSED之前崩溃。当它再次启动时,它将找到相同的PENDING记录并再次发布。有没有一种方法可以避免这种重复,或者唯一的方法就是设计一个幂等系统。
答案 0 :(得分:0)
为防止出现第一个问题,您必须使用数据库锁定。
SELECT * FROM outbox WHERE id = 1 FOR UPDATE
这将防止其他进程访问同一行。
您无法解决的第二个问题,因为您没有使用Kafka进行分布式交易。
因此,一种方法可能是将记录发送到Kafka之前将记录设置为PROCESSING之类的状态,如果应用程序崩溃,则应检查状态是否为PROCESSING的记录,并执行一些清理任务以查明它们是否已经存在发送给卡夫卡。
但是最好的解决方案是拥有一个能够处理重复项的幂等系统。
答案 1 :(得分:0)
您可以使用debezium
(https://debezium.io/)来读取SQL Server的二进制日志并将事件写入Kafka。它将解决您的两个问题。