发件箱模式-如何防止邮件中继过程生成重复的邮件?

时间:2019-06-11 11:33:54

标签: apache-kafka spring-cloud spring-cloud-stream

实现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记录并再次发布。有没有一种方法可以避免这种重复,或者唯一的方法就是设计一个幂等系统。

2 个答案:

答案 0 :(得分:0)

为防止出现第一个问题,您必须使用数据库锁定。

SELECT * FROM outbox WHERE id = 1 FOR UPDATE

这将防止其他进程访问同一行。

您无法解决的第二个问题,因为您没有使用Kafka进行分布式交易。

因此,一种方法可能是将记录发送到Kafka之前将记录设置为PROCESSING之类的状态,如果应用程序崩溃,则应检查状态是否为PROCESSING的记录,并执行一些清理任务以查明它们是否已经存在发送给卡夫卡。

但是最好的解决方案是拥有一个能够处理重复项的幂等系统。

答案 1 :(得分:0)

您可以使用debeziumhttps://debezium.io/)来读取SQL Server的二进制日志并将事件写入Kafka。它将解决您的两个问题。