Web Beans 分布式事件

发布者:    |       CDI

Web Beans 事件总线提供了一种非常好的方法,使有状态的组件能够与应用程序中发生的变化同步其状态。

假设我们有一个Web Bean,用于更新产品目录。以下方法负责创建新的产品产品

public class ProductManager {

   @PersistenceContext EntityManager entityManager;
   @Fires @Created Event<Product> productCreated;

   public void create(Product prod) {
      entityManager.persist(prod);
      productCreated.fire(prod);
   }

   ...

}

当创建一个产品时,Web Bean 会触发一个类型为产品的事件,绑定类型为@Created.

假设有一个第二个Web Bean缓存了产品目录

@ApplicationScoped
public class Catalog {
   List<Product> products;
   
   ...
   
   @Produces List<Product> getProducts() {
      if (products==null) {
         products = getProductsFromDatabase();
      }
      return products;
   }
   
   void productCreated(@Observes @AfterTransactionCompletion @Created Product prod) {
      products.add(prod);
   }
   
}

在内存中,以最小化对数据库的访问产品当在数据库中成功创建一个新的目录时,它会收到事件通知并将新创建的产品添加到其缓存中。

在像目录这样的有状态对象环境中,事件处理比只有无状态对象(如有状态会话Bean)的环境更为重要。因此,我相信事件处理是Web Beans的一个真正必备功能。

然而,Web Beans 公共草案定义的事件功能是一个纯本地构造,当应用程序分布在多个集群节点或多个物理层时,它并不能提供帮助。我们已经确定这是一个必须解决的问题,以解决修订后的公共草案。

设计一个分布式事件总线可能是一个非常艰巨的任务,需要考虑很多问题:QoS、异步性、事务性、路由等。幸运的是,JMS已经拥有了我们需要的功能,并且是今天有时用来解决我们谈论的问题的方法。JMS功能包括

  • 异步、分布式语义
  • 1-of-N和N-of-N接收者(队列和主题)的概念
  • 逻辑通道的概念(支持层和应用程序之间的路由)
  • 支持一系列QoS语义,包括
  • 事务性

此外,JMS在EE环境中得到了很好的理解和支持。

因此,我建议规范只简单地定义事件可以通过JMS进行分发。

应用开发者不会接触到JMS API。与JMS的实际交互将由容器处理。开发者需要做的只是指定具有特定类型和绑定类型的事件将通过某个队列或主题进行分发。

当然,没有任何阻止供应商也支持作为专有扩展的替代分发机制(例如,在JBoss中,我们可以提供对JGroups的支持)。

我在想以下内容

异步事件观察者

事件观察者可以指定它们异步接收事件

void productCreated(@Observes @Asynchronously @Created Product product) { ... }

默认情况下,异步观察者是一个纯粹的本地区构。然而,与同步观察者不同,异步观察者是在与事件触发时不同的web beans上下文中被调用的。

将事件类型映射到JMS通道

JMS主题或队列声明可以指定一组通过该队列/主题分发的事件

<Topic>
   <destination>java:comp/env/jms/CacheInvalidationEvents</destination>
   <connectionFactory>java:comp/env/jms/TopicConnectionFactory</connectionFactory>
   <events>
      <myapp:Product>
         <myapp:Created/>
      </myapp:Product>
   </events>
</Topic>

此声明意味着任何可以分配给指定类型和绑定类型的任何事件都是分布式事件,由命名的JMS主题分发。

由于JMS是事务性介质,我们可以支持进一步设置,将事件以事务方式写入队列/主题。但是,此设置需要是针对事件类型的全局设置。也许是一个@Transactional注解的事件类型?

分布式事件的观察者

容器将验证所有通过JMS分发的任何事件的观察者都声明为异步观察者,如果有任何同步观察者,则抛出异常。

我们需要仔细思考异步事务完成事件观察者的概念。以下组合在语义上是正确的,并且需要支持,即使是对于分布式情况

@Observes @Asynchronously @AfterTransactionCompletion
@Observes @Asynchronously @AfterTransactionSuccess
@Observes @Asynchronously @AfterTransactionFailue

然而,这个组合是没有意义的

@Observes @Asynchronously @BeforeTransactionCompletion

当然,这一切都在进行中,我很乐意听取对这个建议的反馈 :-)


回到顶部