摘要:前言之前使用的事件機(jī)制來(lái)改造系統(tǒng),完成了部分模塊的解耦。但是實(shí)際使用時(shí)卻發(fā)現(xiàn)存在以下問(wèn)題當(dāng)批量推送時(shí),如果在處理的過(guò)程中拋出異常,則會(huì)導(dǎo)致后續(xù)的推送中斷。但是實(shí)際上卻是拋出異常會(huì)導(dǎo)致后續(xù)事件的推送中斷。
前言
之前使用Spring的事件機(jī)制來(lái)改造系統(tǒng),完成了部分模塊的解耦。但是實(shí)際使用時(shí)卻發(fā)現(xiàn)存在以下問(wèn)題:
當(dāng)ApplicationEventPublisher批量推送ApplicationEvent時(shí),如果ApplicationListener在處理的過(guò)程中拋出異常,則會(huì)導(dǎo)致后續(xù)的推送中斷。
PS:Spring版本為5.1.5.RELEASE
下面將會(huì)展示一個(gè)復(fù)盤(pán)的示例
復(fù)盤(pán)示例 自定義事件import org.springframework.context.ApplicationEvent; /** * 自定義事件 * @author RJH * create at 2018/10/29 */ public class SimpleEvent extends ApplicationEvent { private int i; /** * Create a new ApplicationEvent. * * @param source the object on which the event initially occurred (never {@code null}) */ public SimpleEvent(Object source) { super(source); i=Integer.valueOf(source.toString()); } public int getI() { return i; } }事件監(jiān)聽(tīng)器
import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * 自定義事件監(jiān)聽(tīng)器 * @author RJH * create at 2018/10/29 */ @Component public class SimpleEventListener implements ApplicationListener事件推送{ @Override public void onApplicationEvent(SimpleEvent event) { if(event.getI()%10==0){ throw new RuntimeException(); } System.out.println("Time:"+event.getTimestamp()+" event:"+event.getSource()); } }
import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * 事件推送 * @author RJH * create at 2018/10/29 */ public class EventApplication { public static void main(String[] args) { //掃描特定package ApplicationContext context=new AnnotationConfigApplicationContext("com.rjh.event"); for(int i=1;i<=100;i++){//批量推送事件 context.publishEvent(new SimpleEvent(i)); } } }運(yùn)行結(jié)果
Time:1553607971143 event:1 Time:1553607971145 event:2 Time:1553607971145 event:3 Time:1553607971145 event:4 Time:1553607971145 event:5 Time:1553607971145 event:6 Time:1553607971146 event:7 Time:1553607971146 event:8 Time:1553607971146 event:9 Exception in thread "main" java.lang.RuntimeException at com.rjh.event.SimpleEventListener.onApplicationEvent(SimpleEventListener.java:17) at com.rjh.event.SimpleEventListener.onApplicationEvent(SimpleEventListener.java:11) at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:393) at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:347) at com.rjh.event.EventApplication.main(EventApplication.java:17)分析
期待結(jié)果為SimpleEventListener拋出異常不影響EventApplication中后續(xù)事件的推送。但是實(shí)際上卻是SimpleEventListener拋出異常會(huì)導(dǎo)致EventApplication后續(xù)事件的推送中斷。從這里可以看出事件的推送和事件的監(jiān)聽(tīng)是同步阻塞進(jìn)行,而并非是異步。詳細(xì)可以參考文檔中的介紹:
Notice that ApplicationListener is generically parameterized with the type of your custom event (BlackListEvent in the preceding example). This means that the onApplicationEvent() method can remain type-safe, avoiding any need for downcasting. You can register as many event listeners as you wish, but note that, by default, event listeners receive events synchronously. This means that the publishEvent() method blocks until all listeners have finished processing the event. One advantage of this synchronous and single-threaded approach is that, when a listener receives an event, it operates inside the transaction context of the publisher if a transaction context is available. If another strategy for event publication becomes necessary, See the javadoc for Spring’s ApplicationEventMulticaster interface.解決辦法
將事件監(jiān)聽(tīng)改造為異步處理,這里將會(huì)展示基于JavaConfig即注解的解決方案
開(kāi)啟異步import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; /** * 開(kāi)啟異步服務(wù)配置類(lèi) * @author RJH * create at 2019-03-26 */ @EnableAsync @Configuration public class AsyncConfig { }異步事件監(jiān)聽(tīng)
import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; /** * 異步事件監(jiān)聽(tīng) * @author RJH * create at 2019-03-26 */ @Component public class AsyncSimpleEventListener { @EventListener @Async public void handleEvent(SimpleEvent event){ if(event.getI()%10==0){ throw new RuntimeException(); } System.out.println("Time:"+event.getTimestamp()+" event:"+event.getSource()); } }運(yùn)行結(jié)果
Time:1553614469990 event:1 Time:1553614470007 event:72 Time:1553614470006 event:64 Time:1553614470006 event:67 Time:1553614470007 event:73 Time:1553614470007 event:71 Time:1553614470007 event:75 Time:1553614470006 event:68 Time:1553614470007 event:69 Time:1553614470006 event:62 Time:1553614470005 event:61 Time:1553614470006 event:63 Time:1553614470006 event:65 Time:1553614470007 event:74 Time:1553614470006 event:66 Time:1553614470005 event:59 Time:1553614470005 event:57 Time:1553614470005 event:55 Time:1553614470005 event:58 Time:1553614470004 event:51 Time:1553614470004 event:52 Time:1553614470002 event:43 Time:1553614470004 event:53 Time:1553614470002 event:38 Time:1553614470001 event:36 Time:1553614470004 event:54 Time:1553614470001 event:33 Time:1553614470000 event:29 Time:1553614470000 event:27 Time:1553614470005 event:56 Time:1553614469999 event:23 Time:1553614469999 event:22 Time:1553614469999 event:21 三月 26, 2019 11:34:30 下午 org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler handleUncaughtException 嚴(yán)重: Unexpected error occurred invoking async method: public void com.rjh.event.AsyncSimpleEventListener.handleEvent(com.rjh.event.SimpleEvent) Time:1553614470000 event:24java.lang.RuntimeException at com.rjh.event.AsyncSimpleEventListener.handleEvent(AsyncSimpleEventListener.java:19) at com.rjh.event.AsyncSimpleEventListener$$FastClassBySpringCGLIB$$61742dbf.invoke(分析) Time:1553614469998 event:15 at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:736) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:115) at java.util.concurrent.FutureTask.run(FutureTask.java:266) ...內(nèi)容過(guò)長(zhǎng)省略部分結(jié)果
改造為異步執(zhí)行后,事件監(jiān)聽(tīng)就由線程池進(jìn)行處理,此處還可以通過(guò)自定義線程池,并設(shè)置異常處理器來(lái)處理未捕獲的異常。
參考資料https://docs.spring.io/spring...
https://docs.spring.io/spring...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/73959.html
摘要:如問(wèn)到是否使用某框架,實(shí)際是是問(wèn)該框架的使用場(chǎng)景,有什么特點(diǎn),和同類(lèi)可框架對(duì)比一系列的問(wèn)題。這兩個(gè)方向的區(qū)分點(diǎn)在于工作方向的側(cè)重點(diǎn)不同。 [TOC] 這是一份來(lái)自嗶哩嗶哩的Java面試Java面試 32個(gè)核心必考點(diǎn)完全解析(完) 課程預(yù)習(xí) 1.1 課程內(nèi)容分為三個(gè)模塊 基礎(chǔ)模塊: 技術(shù)崗位與面試 計(jì)算機(jī)基礎(chǔ) JVM原理 多線程 設(shè)計(jì)模式 數(shù)據(jù)結(jié)構(gòu)與算法 應(yīng)用模塊: 常用工具集 ...
摘要:前言一些問(wèn)題的整理,平時(shí)實(shí)際工作中可能會(huì)忽視的一些原理性問(wèn)題,后續(xù)會(huì)選取一些有意思的點(diǎn)進(jìn)行詳述。 前言 一些問(wèn)題的整理,平時(shí)實(shí)際工作中可能會(huì)忽視的一些原理性問(wèn)題,后續(xù)會(huì)選取一些有意思的點(diǎn)進(jìn)行詳述。 JAVA多線程、并發(fā)相關(guān) 多個(gè)線程同時(shí)讀寫(xiě),讀線程的數(shù)量遠(yuǎn)遠(yuǎn)?于寫(xiě)線程,你認(rèn)為應(yīng)該如何解決 并發(fā)的問(wèn)題?你會(huì)選擇加什么樣的鎖? JAVA的AQS是否了解,它是?嘛的? 除了synchron...
摘要:結(jié)構(gòu)型模式適配器模式橋接模式裝飾模式組合模式外觀模式享元模式代理模式。行為型模式模版方法模式命令模式迭代器模式觀察者模式中介者模式備忘錄模式解釋器模式模式狀態(tài)模式策略模式職責(zé)鏈模式責(zé)任鏈模式訪問(wèn)者模式。 主要版本 更新時(shí)間 備注 v1.0 2015-08-01 首次發(fā)布 v1.1 2018-03-12 增加新技術(shù)知識(shí)、完善知識(shí)體系 v2.0 2019-02-19 結(jié)構(gòu)...
摘要:本文會(huì)以引出問(wèn)題為主,后面有時(shí)間的話,筆者陸續(xù)會(huì)抽些重要的知識(shí)點(diǎn)進(jìn)行詳細(xì)的剖析與解答。敬請(qǐng)關(guān)注服務(wù)端思維微信公眾號(hào),獲取最新文章。 原文地址:梁桂釗的博客博客地址:http://blog.720ui.com 這里,筆者結(jié)合自己過(guò)往的面試經(jīng)驗(yàn),整理了一些核心的知識(shí)清單,幫助讀者更好地回顧與復(fù)習(xí) Java 服務(wù)端核心技術(shù)。本文會(huì)以引出問(wèn)題為主,后面有時(shí)間的話,筆者陸續(xù)會(huì)抽些重要的知識(shí)點(diǎn)進(jìn)...
摘要:是一個(gè)相對(duì)比較新的微服務(wù)框架,年才推出的版本雖然時(shí)間最短但是相比等框架提供的全套的分布式系統(tǒng)解決方案。提供線程池不同的服務(wù)走不同的線程池,實(shí)現(xiàn)了不同服務(wù)調(diào)用的隔離,避免了服務(wù)器雪崩的問(wèn)題。通過(guò)互相注冊(cè)的方式來(lái)進(jìn)行消息同步和保證高可用。 Spring Cloud 是一個(gè)相對(duì)比較新的微服務(wù)框架,...
閱讀 3550·2021-09-09 11:39
閱讀 1299·2021-09-09 09:33
閱讀 1200·2019-08-30 15:43
閱讀 618·2019-08-29 14:08
閱讀 1794·2019-08-26 13:49
閱讀 2451·2019-08-26 10:09
閱讀 1608·2019-08-23 17:13
閱讀 2357·2019-08-23 12:57