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

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

问题描述

我有一个服务对象,看起来像一个界面如下(简化为简洁起见):

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部分使用,由code处理Ajax请求,创建和修改项目在我们的数据存储对象。

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索引更新,或将消息发送到管理员,让他们知道事情已经改变。 (使用每一个方法调用构成春季单个事务 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 维护,事件得到由 ComposingService 一旦所有的来电 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?

推荐答案

总结你的问题:你正在寻找一个事务安全的方式发送邮件

Summarizing your question: You're looking for a transactionally-safe way to send messages.

事务安全的消息正是JMS是。还有春好JMS集成,见Spring文档在 JMS章

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 ,而在 afterCommit()你的 TransactionSynchronization 发送消息。
可以要么每个事件添加新的同步发送,或添加一个同步和跟踪哪些到通过结合使用含有该列表到交易对象 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).

与当前设置的差异:


  • 如果有异常,在这种情况下,事件的处理抛出,您的服务将抛出异常,但这些变化将已经被数据库中的承诺。

  • 如果你的听众之一,也写入数据库,它会在一个新的事务来这样做。

另外,你可以使用 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天全站免登陆