Java Worker线程在负载平衡的Web应用程序中



我有一个Web应用程序,该应用程序在某些操作中发送电子邮件。因此,为此,它将请求保存在数据库中,然后一个电子邮件工作线程接收请求并发送电子邮件。电子邮件工作者观看数据库以进行更改。

现在,我想在加载平衡器共享相同数据库后面运行相同的Web应用程序。现在的问题是,当我将在数据库中创建电子邮件请求时,有可能在Load Balancer背后的不同机器上运行的电子邮件工作者可能会同时看到电子邮件的数据库输入,这将结果在多次发送的同一电子邮件中。

那么,除了明确锁定桌子外,是否有任何方法可以防止这种情况?

我已经阅读了这个问题,该问题在多个服务器上分发了Java线程? 但是不知道那里提供的解决方案是否足以满足我的需求。Terracotta似乎是解决方案,但我认为它需要明确的同步才能添加到代码中,不知道。

对此的任何知识都会有所帮助。

一个简单的低技术解决方案是让电子邮件发送工人一次。

这可以通过将工人提取到单独的应用程序中来实现,例如,该应用程序是由Cron触发的,或通过使其可配置的,无论您的Web应用程序的实例是否已激活了电子邮件工作。在后一种情况下

不利的一面是,电子邮件发送不会是多余的,即,如果电子邮件发送的主机处于活动状态,则没有人发送任何电子邮件。

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

对于自己的实现,您可以使用hibernate/jpa支持的版本编号字段/列来查看乐观的锁定(请参阅https://stackoverflow.com/a/19456821/981913(。该算法将是以下几行:

  1. 工人醒来,在DB中找到电子邮件请求,版本列= 0
  2. Worker更新版本= 1的请求。仅在版本为0时更新才能成功,这意味着没有其他工人将其更新为1由于请求在步骤1中读取。
  3. 如果步骤2的更新成功,则工人可以安全地假设没有其他工人会处理此要求,然后继续发送电子邮件

使用SQL Transactions锁定一行以进行处理。

您的电子邮件行应该添加2列,时间戳类型:processed_time,send_time,但默认为null

  1. 开始交易
  2. 选择1个项目processed_time为null,而send_time为null并为该数据库使用最高级别的序列化(请参见SQL Server中的内容,我如何以类似于Oracle的"选择更新Wait&quot"的方式锁定单行;?(,每个db/orm都有这样做的方法,Posttre使用选择进行更新等
  3. 更新行并将processed_time设置为现在((
  4. 提交(但不要完成交易,如果电子邮件失败,则可能需要处理或回滚4a。在这一点上,您已经声称自己是自己的行
  5. 发送电子邮件
  6. 更新行并将send_time设置为now((
  7. 结束交易

您可能需要基于您使用的DB/ORM微调此过程,但总体想法保留。

最新更新