在事务结束时发送事件 [英] Sending events at the end of a transaction

查看:156
本文介绍了在事务结束时发送事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Service对象的接口,看起来像下面这样(简化为简洁):

I have an interface for a Service object that looks something like the following (simplified for brevity):

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 对象。

ItemService a single implementation, and is wired up as a Spring bean. It gets used by the UI portion of our project, and by the code that handles Ajax requests, to create and modify Item objects in our data store.

每个方法在引导下发送一系列事件。事件由其他模块接收,以使Lucene索引保持最新,或向管理员发送消息,让他们知道某些内容发生变化。每个方法调用在Spring中构成单个事务(使用 org.springframework.orm.hibernate3.HibernateTransactionManager org.springframework.transaction.interceptor.TransactionProxyFactoryBean )。

Under the hood each method sends out a series of Events when it gets called. The Events are received by other modules to do things like keep Lucene indexes up to date, or to send messages to administrators to let them know something has changed. Each method call constitutes a single transaction in Spring (using org.springframework.orm.hibernate3.HibernateTransactionManager and org.springframework.transaction.interceptor.TransactionProxyFactoryBean).

最近,需要在单个事务中将多个方法调用组合起来。有时有多个服务。例如,我们可能想要执行以下操作:

Recently there has been a need to compose a number of method calls together in a single transaction. Sometimes with more than one Service. For example, we might want to do something like:

*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* 

我们已经通过创建另一个服务来完成此任务,您可以使用父服务中的单一方法从服务中组合调用。让我们称之为 ComposingService 。与所有其他人一样, ComposingService 也由Spring管理,因为交易是可重入的,所有这些都可以工作。

We've done this by creating another Service which allows you to compose calls from Services in a single method in the parent service. Lets call it ComposingService. ComposingService, as with all the others, is also managed by Spring, and as transactions are reentrant, this should all work..

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

However, there is a problem: if any of those operations within the transaction fail, causing the transaction to roll back we do not want to send out any Events whatsoever.

如果交易失败,如果事务中途失败,则事件一半将在事务发送之前由 ItemService 发送回滚,这意味着一些模块将收到一堆事件没有发生的事情。

As it stands, if the transaction fails halfway through, half the Events will be sent by ItemService before the transaction rolls back, meaning that some modules will receive a bunch of Events for things that haven't happened.

我们试图找到一些解决方法,但是我们一直无法想到任何优雅。到目前为止,我们已经提出的最好的就是这样(这很丑):

We are trying to find some way of fixing this, but we've been unable to think of anything elegant. The best we've come up with so far, is something like this (and it's ugly):

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 维护,并且事件获得 ComposeService 发送一次所有调用 ItemService 和其他服务已成功退出。

In this modified ItemService, instead of the Events being sent right away, they are added to the List of Events passed in as an argument. The list is maintained by ComposingService, and the Events get sent by ComposingService once all the calls to ItemService and other services have exited successfully.

显然,问题是我们以一种丑陋的方式改变了ItemService的合同。调用类,即使是服务,也不必担心管理事件。但是我已经无法想到这个方法了,所以这个问题。

Obviously, the problem is that we've changed the contract on ItemService in an ugly manner. Calling classes, even if they are services, should not have to worry about managing Events. But I've been unable to think of a way around this, hence this question.

这看起来像以前可能解决过的那种问题。有没有人有一个类似的问题,如果是这样,你是如何解决的?

This looks like the kind of problem that has probably been solved before. Has anyone had a problem that looks similar, and if so, how did you resolve it?

推荐答案

总结你的问题:

事务安全的消息传递是究竟JMS是什么在Spring中还有很好的JMS集成,请参阅 JMS章节在Spring文档中。

Transactionally safe messaging is exactly what JMS is for. There's also good JMS integration in Spring, See the JMS chapter in the Spring documentation.

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

That will make sure that the messages are sent if and only if the transaction is committed. It also helps for dealing with errors in listeners for these events.

与当前设置的区别在于这些事件将被异步处理:您的服务将在这些事件处理之前返回。 (JMS将确保最终处理它们,可以配置为尝试多次,如何处理错误...)。

A difference with your current setup is that these events will be handled asynchronously: your service will return before these events have been handled. (JMS will make sure that they are processed eventually, it can be configured to try multiple times and how to deal with errors,...). Depending on your needs, that may be a good or a bad thing.

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

Alternatively, if JMS is too heavy-weight for your case, you could use transaction synchronization: When sending an event, instead of sending it directly use Spring's TransactionSynchronizationManager.registerSynchronization, and send the message in the afterCommit() of your TransactionSynchronization. You could either add a new synchronization per event to be sent, or add one synchronization and keep track of which events to be sent by binding an object containing that list to the transaction using TransactionSynchronizationManager.bindResource.

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

I would advise against trying to use your own ThreadLocal for this, because that would go wrong in some cases; for example if inside your transaction you would start a new transaction (RequiresNew).

与您当前的设置不同:

Differences with your current setup:


  • 如果在这种情况下处理事件时抛出异常,您的服务将抛出异常,但更改将已经已在数据库中提交。

  • 如果您的一个监听器也写入数据库,则必须在新的事务中执行此操作。

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

Alternatively, you can use beforeCommit instead of afterCommit, but then your events will be handled (mails sent,...) even if the actual commit to the database later fails.

这不太可靠(更少的事务性),使用JMS,但更轻,更容易设置,通常

This is less robust (less transactional), than using JMS, but lighter and easier to set up, and usually good enough.

这篇关于在事务结束时发送事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆