2019独角兽企业重金招聘Python工程师标准>>>

CDI事件本身是属于同步的,但在一些场景下,你可能会需要异步的CDI events。本文将概述异步cdi event事件。

1.Default Synchronous Events

先看一个基本的例子

@Path("/produce")
public class EventGenerator {@Injectprivate Logger logger;@Injectprivate Event<MyEvent> events;@Path("/cdiBean/{eventsNum}")@GETpublic String generateEvents(@PathParam("eventsNum") int numberOfEventsToGenerate) {for (int i = 0; i < numberOfEventsToGenerate; i++) {MyEvent event = new MyEvent(i);logger.info("Generating Event: " + event);events.fire(event);}return "Finished. Generated " + numberOfEventsToGenerate + " events.";}
}

MyEvent只是一些事件对象,它在这里不是很重要。
消费者是一个非常简单的CDI bean:

public class EventConsumer {@Injectprivate Logger logger;public void consumeEvent(@Observes MyEvent myEvent) throws InterruptedException {logger.info("Receiving event: " + myEvent);TimeUnit.MILLISECONDS.sleep(500);}
}

请注意,我写一个线程睡眠来模拟一些长时间运行的事件接收器模拟复杂费时的处理过程。
现在,让我们通过调用REST命令,这是EventProducer露出运行这个例子。

14:15:59,196 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=0]
14:15:59,197 [com.piotrnowicki.EventConsumer] (http-/127.0.0.1:8080-1) Receiving event: MyEvent[seqNo=0]
14:15:59,697 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=1]
14:15:59,698 [com.piotrnowicki.EventConsumer] (http-/127.0.0.1:8080-1) Receiving event: MyEvent[seqNo=1]
14:16:00,199 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=2]
14:16:00,200 [com.piotrnowicki.EventConsumer] (http-/127.0.0.1:8080-1) Receiving event: MyEvent[seqNo=2]

通过观察,我们可以得到结论,其cdi event默认就是同步的。下面我们将折腾异步的。

Solution 1 – CDI Producer and Singleton EJB as Receiver

我们的生产者保持不变

@Path("/produce") public class EventGenerator {@Path("/cdiBean/{eventsNum}")@GETpublic String generateEvents(@PathParam("eventsNum") int numberOfEventsToGenerate) { ... }
}

现在你把接受者设置为 @Singleton EJB,并且标记oberves方法为@Asynchronous

@Singleton
public class EventConsumer {@Asynchronouspublic void consumeEvent(@Observes MyEvent myEvent) throws InterruptedException { ...  }
}

你将得到以下结果

14:21:19,341 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=0]
14:21:19,343 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=1]
14:21:19,343 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=2]
14:21:19,347 [com.piotrnowicki.EventConsumer] (EJB default – 2) Receiving event: MyEvent[seqNo=1]
14:21:19,848 [com.piotrnowicki.EventConsumer] (EJB default – 1) Receiving event: MyEvent[seqNo=0]
14:21:20,350 [com.piotrnowicki.EventConsumer] (EJB default – 3) Receiving event: MyEvent[seqNo=2]

事件一个接一个被Singleton EJB在单独的线程处理(看看时间事件处理。), 其实这里的consumeEvent是隐式的write:
异步:是
Ob方法是线程安全的:是

Solution 2 – Use Singleton EJB as Receiver With Read Lock

这种方法是和解决方案1非常相似的,但是,它提供了高得多的吞吐量,因为所有的事件平行处理。produce仍然不变。

@Path("/produce")
public class EventGenerator {@Path("/cdiBean/{eventsNum}")@GETpublic String generateEvents(@PathParam("eventsNum") int numberOfEventsToGenerate) { ... }
}

我们的接收者的OB上有@Lock(READ);这使得能够在同一时间服务于多个事件(而在一中是Lock(Write)):

@Singleton
public class EventConsumer {@Asynchronous@Lock(LockType.READ)public void consumeEvent(@Observes MyEvent myEvent) throws InterruptedException { ... }
}

result:

14:24:44,202 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=0]
14:24:44,204 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=1]
14:24:44,205 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=2]
14:24:44,207 [com.piotrnowicki.EventConsumer] (EJB default – 4) Receiving event: MyEvent[seqNo=0]
14:24:44,207 [com.piotrnowicki.EventConsumer] (EJB default – 6) Receiving event: MyEvent[seqNo=2]
14:24:44,207 [com.piotrnowicki.EventConsumer] (EJB default – 5) Receiving event: MyEvent[seqNo=1]

因此,我们结论是,这样的写法给你更大的吞吐量.

异步:是 
Ob方法是线程安全的:不是

Solution 3 – EJB Producer and CDI Consumer

CDI可以让你在事务的特定阶段去处理观察者位置的事件。
你可以指定使用@Observes(during=TransactionPhase...).
在我们的例子中,我们希望CDI观察者事件在事务结束之后才进行。要做到这一点,我们只需要把属性添加到我们的CDI Bean的观察者方法注解上:

public class EventConsumer { public void consumeEvent(@Observes(during = TransactionPhase.AFTER_COMPLETION) MyEvent myEvent) { ... }
}

现在我们只需要确认我们在Event Generator方法运行的事务。我们可以通过改变我们的CDI Bean成为EJB @Stateless 和使用其隐含的事务--REQUIRED TransactionAttribute:

@Stateless
@Path("/produce")
public class EventGenerator {@Path("/cdiBean/{eventsNum}")@GETpublic String generateEvents(@PathParam("eventsNum") int numberOfEventsToGenerate) { ... }
}

结果:

14:39:06,776 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=0]
14:39:06,776 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=1]
14:39:06,776 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=2]
14:39:06,778 [com.piotrnowicki.EventConsumer] (http-/127.0.0.1:8080-1) Receiving event: MyEvent[seqNo=2]
14:39:07,279 [com.piotrnowicki.EventConsumer] (http-/127.0.0.1:8080-1) Receiving event: MyEvent[seqNo=1]
14:39:07,780 [com.piotrnowicki.EventConsumer] (http-/127.0.0.1:8080-1) Receiving event: MyEvent[seqNo=0]

EJB Event Generator启动一个事务,CDI bean的观察者将在事务完成后,才可以调用。

Asynchronous: yes
Thread-safe observer method: yes

Solution 4 – EJB Producer and EJB Consumer

与3类似

@Stateless
@Path("/produce")
public class EventGenerator {@Path("/cdiBean/{eventsNum}")@GETpublic String generateEvents(@PathParam("eventsNum") int numberOfEventsToGenerate) { ... }
}

但是更改了观察者方法

@Singleton
public class EventConsumer {@Asynchronous@Lock(LockType.READ)public void consumeEvent(@Observes(during = TransactionPhase.AFTER_COMPLETION) MyEvent myEvent) throws InterruptedException { ...  }
}

result

14:44:09,363 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=0]
14:44:09,464 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=1]
14:44:09,564 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=2]
14:44:09,670 [com.piotrnowicki.EventConsumer] (EJB default – 8) Receiving event: MyEvent[seqNo=2]
14:44:09,670 [com.piotrnowicki.EventConsumer] (EJB default – 2) Receiving event: MyEvent[seqNo=1]
14:44:09,670 [com.piotrnowicki.EventConsumer] (EJB default – 1) Receiving event: MyEvent[seqNo=0]

我们在这里使用的两个特点 - 一是该事件消费者的方法是异步的,第二个是生产者事务完成之前,消费者将不会通知。
Asynchronous: yes
Thread-safe observer method: no

Solution 4 vs Solution 2

这两个解决方案似乎是相同的。他们只是消费者的注释不同:@Observes vs @Observes(during = TransactionPhase.AFTER_COMPLETION). 
我们的测试的情况表明它们的作用是相同的:它们是异步的,并且多个线程可以在同一时间进行事件处理。但是,他们之间有一个很大的不同。
在我们的测试情况下,我们触发的事件一个接一个。试想一下,有一些其它的操作之间的事件触发。在这种情况下:
Solution 2 (@Observes)在第一个就会fire,启动处理events.
Solution 4 (@Observes(during = TransactionPhase.AFTER_COMPLETION)) 它只会在事务完成后开始处理,所以那个时候所有的events将被fired.

Solution 2 (@Observes)15:01:34,318 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=0]
15:01:34,320 [com.piotrnowicki.EventConsumer] (EJB default – 3) Receiving event: MyEvent[seqNo=0]
15:01:34,419 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=1]
15:01:34,420 [com.piotrnowicki.EventConsumer] (EJB default – 6) Receiving event: MyEvent[seqNo=1]
15:01:34,520 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=2]
15:01:34,521 [com.piotrnowicki.EventConsumer] (EJB default – 9) Receiving event: MyEvent[seqNo=2]
Solution 4 (@Observes(during = TransactionPhase.AFTER_COMPLETION))15:00:41,126 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=0]
15:00:41,226 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=1]
15:00:41,326 [com.piotrnowicki.EventGenerator] (http-/127.0.0.1:8080-1) Generating Event: MyEvent[seqNo=2]
15:00:41,432 [com.piotrnowicki.EventConsumer] (EJB default – 10) Receiving event: MyEvent[seqNo=2]
15:00:41,432 [com.piotrnowicki.EventConsumer] (EJB default – 4) Receiving event: MyEvent[seqNo=1]
15:00:41,432 [com.piotrnowicki.EventConsumer] (EJB default – 5) Receiving event: MyEvent[seqNo=0]

Solution 5 – EJB Producer and CDI Consumer II

上述我们一直让观察者异步.我们也可以让事件生产者异步.

@Stateless
@Path("/produce")
public class EventGenerator {// ...@Resourceprivate SessionContext sctx;@Path("/cdiBean/{eventsNum}")@GETpublic String generateEvents(@PathParam("eventsNum") int numberOfEventsToGenerate) {for (int i = 0; i < numberOfEventsToGenerate; i++) {sctx.getBusinessObject(EventGenerator.class).fireEvent(new MyEvent(i));}return "Finished. Generated " + numberOfEventsToGenerate + " events.";}@Asynchronouspublic void fireEvent(final MyEvent event) {events.fire(event);}
}

仔细看看EJB使用sessioncontext。在此情况下,因为我们希望在容器派遣我们方法的调用,并添加了它的异步性质。我们不希望使之成为本地呼叫,所以我们拒绝使用隐含此对象。

public class EventConsumer {public void consumeEvent(@Observes MyEvent myEvent) throws InterruptedException { ... }
}
00:40:32,820 [com.piotrnowicki.EventGenerator] (EJB default – 2) Generating Event: MyEvent[seqNo=1]
00:40:32,820 [com.piotrnowicki.EventGenerator] (EJB default – 3) Generating Event: MyEvent[seqNo=2]
00:40:32,820 [com.piotrnowicki.EventGenerator] (EJB default – 1) Generating Event: MyEvent[seqNo=0]
00:40:32,821 [com.piotrnowicki.EventConsumer] (EJB default – 1) Receiving event: MyEvent[seqNo=0]
00:40:32,821 [com.piotrnowicki.EventConsumer] (EJB default – 2) Receiving event: MyEvent[seqNo=1]
00:40:32,821 [com.piotrnowicki.EventConsumer] (EJB default – 3) Receiving event: MyEvent[seqNo=2]

Asynchronous: yes
Thread-safe observer method: no

Solution 6 – CDI With JMS

https://weblogs.java.net/blog/jjviana/archive/2010/04/13/decoupling-event-producers-and-consumers-jee6-using-cdi-and-jms

转载于:https://my.oschina.net/zhaoqian/blog/351634

Asynchronous CDI Events相关推荐

  1. 一个简单的GTK的例子程序

    源代码: 编译命令和结果: 一些解释说明: The file includes all of the widgets, variables, functions, and structures ava ...

  2. 2020年 ICLR 国际会议最终接受论文(poster-paper)列表(四)

    来源:AINLPer微信公众号(点击了解一下吧) 编辑: ShuYini 校稿: ShuYini 时间: 2020-02-21     2020年的ICLR会议将于今年的4月26日-4月30日在Mil ...

  3. Twain Capabilities 转

    转自:http://blog.csdn.net/pamxy/article/details/8629213 Asynchronous Device Events   异步设备事件 CAP_DEVICE ...

  4. *.rtss程序的运行、终止方式及使用方法

    运用RTSSRun 命令加载RTss程序时,要注意退出,否则当运行的线程超过设定值时(在 RTX Properties -> System tab->processes中设置默认为10), ...

  5. ie浏览器F12查看请求挂起

    转载https://blog.csdn.net/wggoeasy/article/details/45169787 只有在IE(目前只在IE8和IE9中测试)中浏览时,发现有好些资源加载不上,在F12 ...

  6. 一些英文写作里的常见实验表述(持续更新中)

    最近在写论文,整理了一些英文写作里的常用的表达 领域:视频插帧 We summarize the results in 我们总结了-实验结果 surpasses all the others in P ...

  7. TwainCapabilities

    Twain Capabilities 2013年10月15日 ⁄ 综合 ⁄ 共 6098字 ⁄ 字号 小 中 大 ⁄ 评论关闭 转自:http://blog.163.com/lvan100@yeah/ ...

  8. 【Twain协议】Twain协议参数解析

    Asynchronous Device Events   异步设备事件 CAP_DEVICEEVENT MSG_SET选择应用程序希望Twain源报告的事件; MSG_RESET返回Twain源的首选 ...

  9. Twain Capabilities

    转自:http://blog.163.com/lvan100@yeah/blog/static/68117214201111501647802/ Asynchronous Device Events ...

最新文章

  1. 【算法学习笔记】43.动态规划 逆向思维 SJTU OJ 1012 增长率问题
  2. java access 分页查询语句,简单又高效的Access分页语句
  3. php 获取url文件名,php 获取当前访问的url文件名的方法小结
  4. MATLAB中GUI设计的基本操作
  5. 面试题之Memcached与Redis的区别
  6. Mono 2.0正式发布了
  7. java异常个人理解
  8. 如何成为一个Java高薪架构师?
  9. html css做网页总结,学习CSS制作网页总结的一些经验
  10. 时间轴的实现(简单到爆炸)
  11. NSX控制平面和静态路由更新流程1
  12. linux 免sudo,linux 创建用户,免密sudo,ssh免密登录
  13. Camera2点击对焦实现
  14. 添加SAP_ALL权限
  15. 几款swf flv flash网页播放器
  16. vue:ali 阿里矢量图库 ico
  17. python数据分析模型建立_一个完整的数据分析案例 | 用Python建立客户流失预测模型(含源数据+代码)...
  18. Encoded password does not look like BCrypt的原因及解决办法
  19. 正则表达式给查找到的内容加引号
  20. TEEOS的实例-在线支付系统

热门文章

  1. OleDbHelper类
  2. Windows 2008 ×××与 CA
  3. runtime 项目实战方法处理
  4. 网页测试本地服务器_音视频开发搭建一个直播服务器
  5. Arduino可穿戴教程之第一个程序——连接硬件选择板子(二)
  6. Xamarin如何生成Android项目的APK
  7. c语言指针和结构体难点,C语言指针和结构体
  8. mysql 集群切换_完美起航-MySQLMHA高可用集群部署及故障切换(图文详解)
  9. “突然忘记要干啥”有了科学解释!两组神经元在作祟,南大校友一作 | 哈佛医学院多伦多...
  10. Meta为元宇宙建全球最快AI超算,1.6万个A100 GPU,英伟达都赚麻了