SpringBoot中如何优雅地实现异步调用?
前言
SpringBoot想必大家都用过,但是大家平时使用发布的接口大都是同步的,那么你知道如何优雅的实现异步呢?
这篇文章就是关于如何在Spring Boot中实现异步行为的。但首先,让我们看看同步和异步之间的区别。同步编程:在同步编程中,任务一次执行一个,只有当一个任务完成时,下一个任务才会被解除阻塞。异步编程:在异步编程中,可以同时执行多个任务。您可以在上一个任务完成之前转到另一个任务。
在Spring Boot中,我们可以使用@Async注解来实现异步行为。实现步骤定义一个异步服务接口AsyncService.javapublic interface AsyncService { void asyncMethod() throws InterruptedException; Future futureMethod() throws InterruptedException; } 复制代码实现定义的接口AsyncServiceImpl.java@Service @Slf4j public class AsyncServiceImpl implements AsyncService { @Async @Override public void asyncMethod() throws InterruptedException { Thread.sleep(3000); log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName()); } @Async @Override public Future futureMethod() throws InterruptedException { Thread.sleep(5000); log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName()); return new AsyncResult<>("task Done"); } } 复制代码AsyncServiceImpl 是一个 spring 管理的 bean。您的异步方法必须是公共的,而且是被@Async注解修饰。返回类型被限制为 void 或 Future。定义一个控制器AsyncController.java@EnableAsync @RestController @Slf4j public class AsyncController { @Autowired AsyncService asyncService; @GetMapping("/async") public String asyncCallerMethod() throws InterruptedException { long start = System.currentTimeMillis(); log.info("call async method, thread name: [{}]", Thread.currentThread().getName()); asyncService.asyncMethod(); String response = "task completes in :" + (System.currentTimeMillis() - start) + "milliseconds"; return response; } @GetMapping("/asyncFuture") public String asyncFuture() throws InterruptedException, ExecutionException { long start = System.currentTimeMillis(); log.info("call async method, thread name: [{}]", Thread.currentThread().getName()); Future future = asyncService.futureMethod(); // 阻塞获取结果 String taskResult = future.get(); String response = taskResult + "task completes in :" + (System.currentTimeMillis() - start) + "milliseconds"; return response; } } 复制代码关键点,需要添加启用异步的注解@EnableAsync ,当然这个注解加在其他地方也ok得。当外部调用该接口时,asyncMethod() 将由默认任务执行程序创建的另一个线程执行,主线程不需要等待完成异步方法执行。运行一下
现在我们运行一下看看,是不是异步返回的。
可以看到调用/async接口,最终一步调用了方法。
调用/asyncFuture,发现返回5秒多,难道不是异步的吗?其实也是异步的,看日志可以看出来,只不过我们返回的是Future,调用Futrue.get()是阻塞的。自定义异步任务执行器和异常处理
我们现在看看如果异常方法中报错了会怎么样?修改异步代码如下所示,会抛运行时异常:
再次执行异步接口,如下所示,会使用默认的线程池和异常处理。
我们也可以自定义异步方法的处理异常和异步任务执行器,我们需要配置 AsyncUncaughtExceptionHandler,如下代码所示:@Configuration public class AsynConfiguration extends AsyncConfigurerSupport { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(3); executor.setMaxPoolSize(4); executor.setThreadNamePrefix("asyn-task-thread-"); executor.setWaitForTasksToCompleteOnShutdown(true); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { System.out.println("Exception: " + ex.getMessage()); System.out.println("Method Name: " + method.getName()); ex.printStackTrace(); } }; } } 复制代码
再次运行,得到的结果如下:
@Async如何工作的?
必须通过使用 @EnableAsync注解注解主应用程序类或任何直接或间接异步方法调用程序类来启用异步支持。主要通过代理模式实现,默认模式是 Proxy,另一种是 AspectJ。代理模式只允许通过代理拦截调用。永远不要从定义它的同一个类调用异步方法,它不会起作用。
当使用 @Async对方法进行注解时,它会根据"proxyTargetClass"属性为该对象创建一个代理。当 spring 执行这个方法时,默认情况下它会搜索关联的线程池定义。上下文中唯一的 spring 框架 TaskExecutor bean 或名为"taskExecutor"的 Executor bean。如果这两者都不可解析,默认会使用spring框架SimpleAsyncTaskExecutor来处理异步方法的执行。总结
在本文中,我们演示了在 spring boot 中如何使用 @Async 注解和异步方法中的异常处理实现异步行为。我们可以在一个接口中,需要访问不同的资源,比如异步调用各个其他服务的接口,可以使用@Async,然后将结果通过Future的方式阻塞汇总,不失为一个提高性能的好方法。
人间清醒杨绛先生经典语录摘抄思想不在一个高度,尊重就好三观不在一个层次,微笑就好人与人之间最好的相处模式尊重是标配,靠谱是高配,厚道是顶配。顺其自然,一切随缘过去事过去人,笑笑就好现在事现在人,尽心就好未来事
不要忽视他人的力量交流思想说到他人,很容易想到工作伙伴家庭公司还有社群学习,特别是兴趣群,群友友们互相勉励,实现共同发展。在活着出生命的意义中也讲到,作者从奥斯维辛营出来后就是找到了自己活下去的信仰
搭伙过日子,就是最好的爱情故事人生没有回头路,余生就是搭伙过日子了,别再做任何要求了。爱与不爱的都无所谓。不去强求他人,委屈自己,心怀坦荡,余生陪伴,柴米酱醋茶,平凡一生,每天给彼此一个微笑。在生活中,两个人相
戏剧渗透进你我的生活,流传甚广的爱情宣言知道它们出自哪里吗?我想给你一切,可我一无所有,我想为你放下一切,可我又没有什么能够放下钱地位荣耀,我仅有的那一点点尊严没有这些东西装点也就不值一提如果在中世纪,我能够去做一名骑士,把你的名字刻在每一
新时代新征程新伟业一线见闻洪雅阳坪村民情日志里的温情接近岁尾,记者见到洪雅县将军镇阳坪村党委副书记龚学莲时,她正将这一年的便民服务登记表做整理,准备归档。厚厚的一摞纸上,记录着这一年中哪一天哪一个人向村上反映了什么事情,村上是如何处
人民网评标本兼治,严厉打击哄抬药品价格行为近日,有药店连花清瘟单盒卖百元话题冲上热搜引发普遍关注,同样售价飙升的,还有布洛芬缓释胶囊蒲地蓝消炎口服液等清热镇痛药。针对此,广东天津浙江北京等多地市场监管部门相继发布提醒告诫书
这个不起眼的泥疙瘩,竟然藏着中华历史的密码在上海博物馆的展厅里,带有文字的泥疙瘩恐怕是最不起眼的展品,观众感觉陌生。这些两千年前的遗物,字痕和篆印相仿,真实记录着中国古代封建王朝行政活动郡县设立等,并和社会日常如影相随。它
又见红毯,有人身着孕妇装,有人变公主,有人玩科技如果说金鸡百花等红毯走秀像年夜饭,看点是老中青多代艺人欢聚一堂。那么GQ盛典就是年轻人的圣诞舞会,一群俊男靓女们争奇斗艳粉丝疯狂舔屏。由于种种原因,今年的嘉宾名单直到15日下午还有
感冒发烧难受吃它就能好?黄桃罐头真有这么神?最近,黄桃罐头又火了。不仅频频上热搜,超市网店还都卖断货。香甜多汁的黄桃罐头是很多人童年回忆的白月光,生病吃黄桃罐头成为了一种特殊情怀。很多网友说小时候只要一发烧,妈妈就会给我买黄
关心别人,就等于关心自己世界上有好多方法可以使任何人去做任何一件事,大家是否真正停下来静心去考虑过此事?不错,世上有很多方法,能够促使我们愿意去做那一件事。记住,除了这个方法也许再也没有其他方法。当然了,
2022,还有几个这样的年?我想对你说手机屏幕上的钢化玻璃已经碎裂了一部分了。倒映出自己怎么也笑不出来的脸。2022年末,我在山上老家的窗前,在这个并不算冷的冬夜,看着床上熟睡的孩子稚嫩的脸,写下这些文字。网