负载均衡Web应用程序中的Java工作线程

时间:2018-02-12 12:06:25

标签: java thread-safety load-balancing

我有一个Web应用程序,可以发送有关某些操作的电子邮件。因此,为此,它将请求保存在数据库中,然后一个电子邮件工作线程接收请求并发送电子邮件。电子邮件工作人员会在数据库中查看更改。

现在我希望在负载均衡器后面运行相同的Web应用程序共享同一个数据库。现在问题是当我在数据库中创建一个电子邮件请求时,在负载均衡器后面的不同机器上的类似Web应用程序内运行的电子邮件工作者可能会同时看到电子邮件的数据库条目,这将导致在同一封电子邮件中多次发送。

那么,除了显式锁定表之外,还有什么办法可以防止这种情况吗?

我已经阅读了这个问题Distributing java threads over multiple servers? ,但不知道那里提供的解决方案是否足以满足我的需求。 Terracotta似乎是解决方案,但我认为它需要将显式同步添加到代码中,不知道。

任何有关此方面的知识都会有所帮助。

2 个答案:

答案 0 :(得分:0)

简单,低技术的解决方案是让电子邮件发送工作者只运行一次。

这可以通过将工作程序解压缩到单独的应用程序来实现,该应用程序由cron触发,或者通过使其可配置,无论Web应用程序的实例是否已激活电子邮件工作程序。在后一种情况下,您需要确保只有一个负载平衡实例的电子邮件工作者处于活动状态

缺点是电子邮件发送不会是多余的,即如果发送电子邮件的主机已关闭,则没有人发送任何电子邮件。

如果您需要冗余,您必须依赖某些分布式中间件,如上面链接的问题,或者自己实现同步机制。

对于自己的实现,您可以使用电子邮件请求中的版本号字段/列来查看乐观锁定,如Hibernate / JPA所支持(请参阅https://stackoverflow.com/a/19456821/981913)。该算法将如下所示:

  1. 工作人员醒来,在数据库中找到电子邮件请求,版本列= 0
  2. Worker使用version = 1更新请求。如果版本之前为0,则仅更新成功,这意味着没有其他工作程序将其更新为1 因为请求已在步骤1中阅读。
  3. 如果步骤2的更新成功,工作人员可以安全地假设没有其他工作人员会处理此请求,并继续发送电子邮件

答案 1 :(得分:0)

使用SQL事务锁定行以进行处理。

您的电子邮件行应添加2列,TIMESTAMP类型:processed_time,send_time但默认为NULL

  1. 开始交易
  2. 选择1个项目,其中processed_time为NULL且send_time为NULL并使用该数据库的最高级别序列化(请参阅In SQL Server, how can I lock a single row in a way similar to Oracle's "SELECT FOR UPDATE WAIT"?之类的内容),每个DB / ORM都有这样做的方法,postres使用SELECT FOR UPDATE等等。
  3. 更新行并将processed_time设置为NOW()
  4. 提交(但不要关闭事务,如果电子邮件失败,您可能需要处理或回滚 4A。此时你已经为自己声明了这一行,所以另一个线程不会在第2步获得该行,并且需要尽快完成
  5. 发送电子邮件
  6. 更新行并将send_time设置为NOW()
  7. 结束交易
  8. 您可能需要根据您使用的DB / ORM微调此过程,但总体思路仍然存在。