java - 在事务结束时发送事件

标签 java spring architecture design-patterns

我有一个 Service 对象的接口(interface),类似于以下内容(为简洁起见):

public interface ItemService {

  public Item getItemById(String itemId, int version);

  public void create(Item item, User user);

  public void update(Item item, User user);

  public void delete(Item item, User user);

}

ItemService 一个单独的实现,并被连接为一个 Spring bean。它被我们项目的 UI 部分和处理 Ajax 请求的代码用来在我们的数据存储中创建和修改 Item 对象。

在底层,每个方法在被调用时都会发出一系列事件。事件被其他模块接收,以执行诸如保持 Lucene 索引最新的事情,或者向管理员发送消息以让他们知道某些事情发生了变化。每个方法调用在 Spring 中构成一个事务(使用 org.springframework.orm.hibernate3.HibernateTransactionManagerorg.springframework.transaction.interceptor.TransactionProxyFactoryBean)。

最近需要在单个事务中组合多个方法调用。有时有不止一项服务。例如,我们可能想做这样的事情:

*Begin transaction*
Get Items created by User Bill using ItemService
 for each Item in Items 
   Update field on Item
   Link Item to User Bill with LinkService 
   Update Item using ItemService
*Finish transaction* 

我们通过创建另一个服务来做到这一点,它允许您在父服务中的单个方法中组合来自服务的调用。让我们称之为 ComposingServiceComposingService 与所有其他服务一样,也由 Spring 管理,并且由于事务是可重入的,这应该都可以工作..

但是,有一个问题:如果事务中的任何操作失败,导致事务回滚我们不想发送任何事件

就目前而言,如果事务中途失败,一半的事件将在事务回滚之前由 ItemService 发送,这意味着某些模块将收到一堆未完成的事件的事件t 发生了。

我们正在尝试找到解决此问题的方法,但我们一直想不出任何优雅的方法。到目前为止,我们想出的最好的东西是这样的(而且很丑):

public interface ItemService {

  public Item getItemById(String itemId, int version);

  public void create(Item item, User user, List<Event> events);

  public void update(Item item, User user, List<Event> events);

  public void delete(Item item, User user, List<Event> events);

}

在这个修改后的 ItemService 中,不是立即发送事件,而是将它们添加到作为参数传入的事件列表中。该列表由 ComposingService 维护,一旦对 ItemService 和其他服务的所有调用成功退出,事件将由 ComposingService 发送。

显然,问题在于我们以丑陋的方式更改了 ItemService 的契约(Contract)。调用类,即使它们是服务,也不应该担心管理事件。但是我一直无法想出解决这个问题的方法,因此提出了这个问题。

这看起来像是以前可能已经解决的问题。有没有人遇到过类似的问题,如果有,您是如何解决的?

最佳答案

总结您的问题:您正在寻找一种事务安全的方式来发送消息。

选项 1:JMS

事务安全的消息传递正是 JMS 的用途。 Spring 中也有很好的 JMS 集成,请参阅 JMS chapter在 Spring 文档中。

这将确保消息被发送当且仅当事务被提交。 它还有助于处理这些事件的监听器中的错误。

与您当前设置的不同之处在于这些事件将被异步处理:您的服务将在这些事件被处理之前返回。 (JMS 将确保它们最终得到处理,它可以配置为多次尝试以及如何处理错误,...)。根据您的需求,这可能是好事也可能是坏事。

选项 2:事务同步

或者,如果 JMS 对于您的情况来说过于重量级,您可以使用事务同步:发送事件时,不要直接发送它,而是使用 Spring 的 TransactionSynchronizationManager.registerSynchronization ,并在 afterCommit() 中发送消息您的TransactionSynchronization . 您可以为每个要发送的事件添加一个新的同步,或者添加一个同步并通过使用 TransactionSynchronizationManager.bindResource 将包含该列表的对象绑定(bind)到事务来跟踪要发送的事件。 .

我建议不要尝试使用您自己的 ThreadLocal为此,因为在某些情况下会出错;例如,如果在您的事务中,您将开始一个新事务 (RequiresNew)。

与您当前设置的差异:

  • 如果在这种情况下处理事件时抛出异常,您的服务将抛出异常,但更改已经提交到数据库中。
  • 如果您的一个监听器也写入数据库,它必须在新事务中这样做。

或者,您可以使用 beforeCommit而不是 afterCommit ,但是即使稍后对数据库的实际提交失败,您的事件也会被处理(发送的邮件,...)。

这不如使用 JMS 健壮(事务性),但更轻且更易于设置,并且通常足够好

关于java - 在事务结束时发送事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7039333/

相关文章:

java - 在本地创建 spring 和 nodejs 应用程序之间的 SSL 连接

architecture - CQRS 架构优化和变体

java - 如何提取Y、Cb、Cr颜色分量?

java - 仅指定一些包具有调试输出

java - 如何向另一个域发送请求并在 Spring MVC Controller 中获取响应正文?

architecture - 您如何编写基于网络的实时协作工具,例如 google docs?

mysql - 用于处理员工能力的 SQL 架构设计

java - 无法连接到设备 0!原因: Emulator 0 terminated while waiting for it to register!

java - 在 android sqlite 数据库中插入和执行查询时出错

java - hibernate 分页导致选择和更新调用