1.springboot 整合异步任务
与定时任务的套路相同,通过注解来实现。
@EnableAsync,类注解。启动类中,添加该注解,表示开启异步任务。
@Component,类注解。异步任务类中,添加该注解,能让容器扫描到异步任务。
@Async,方法注解。添加到异步任务类的任务方法中,表示该方法是异步方法。
具体实现:
首先,启动类
@EnableAsync public class ThriftdemoApplication { public static void main(String[] args) { SpringApplication.run(ThriftdemoApplication.class, args); } }
然后,异步任务类:
@Component public class AsyncTask { @Async public Future<Boolean> doTask11() throws InterruptedException { long start = System.currentTimeMillis(); Thread.sleep(1000); long end = System.currentTimeMillis(); System.out.println("任务1耗时:"+(end - start)+ "毫秒"); return new AsyncResult<>(true); } @Async public Future<Boolean> doTask22() throws InterruptedException { long start = System.currentTimeMillis(); Thread.sleep(700); long end = System.currentTimeMillis(); System.out.println("任务2耗时:"+(end - start)+ "毫秒"); return new AsyncResult<>(true); } @Async public Future<Boolean> doTask33() throws InterruptedException { long start = System.currentTimeMillis(); Thread.sleep(600); long end = System.currentTimeMillis(); System.out.println("任务6耗时:"+(end - start)+ "毫秒"); return new AsyncResult<>(true); } }
最后,测试异步任务是否实现:
新建异步任务的controller类,通过浏览器访问,计算总的访问耗时时间
@RestController @RequestMapping("tasks") public class AsyncController { @Autowired private AsyncTask asyncTask; @RequestMapping("test1") public String test1() throws InterruptedException { long start = System.currentTimeMillis(); Future<Boolean> a = asyncTask.doTask11(); Future<Boolean> b = asyncTask.doTask22(); Future<Boolean> c = asyncTask.doTask33(); while(!a.isDone()||!b.isDone()||!c.isDone()){ if(a.isDone()&&b.isDone()&&c.isDone()){ break; } } long end = System.currentTimeMillis(); String items = "任务全部完成,总耗时:"+(end -start)+"毫秒"; return items; } }
会发现,整个访问耗时为“任务全部完成,总耗时:1019毫秒”。
如果是同步任务,那么整个耗时应该是1000+700+600> 1019的。同时,我们将同步任务耗时打印一下,应证一下这个结论(去掉注解@Async,就可实现同步任务),可见“任务全部完成,总耗时:2305毫秒”。
同时,也可以发现,不管是异步还是同步,task11,task22,task33这三个子任务的耗时,几乎是不变的。
2.异步任务的使用场景
1.发短信,IM消息,发邮件,运维凌晨发布任务。比如我现在要做一次运营推广任务。任务的内容是向白名单用户通过短信(其实IM消息也是这个道理)方式推广一个运营活动,此时我通过业务注册来源,已经获取到这批白名单用户的手机号,如果我发完一条短信,再接着发第二条,那等我把白名单用户挨个发完短信,这个活动的活动期说不定都过去了。所以,我们要做的是,将这批白名单用户加入到多个消息队列中,通过定时任务,触发处理消息队列的发短信任务,消息就可以批量发出去了。这样就能解决挨个同步发送导致的问题。
2.需要等待回调的且包含多个子任务的项目,可以将该项目的架构设计为异步消息处理系统。(打个比方,可能博客园不是这样实现的)比如审核博客园的博主身份,我在提交一系列证件后,我的博主身份需一段时间查证,因此会通过标识位,让身份状态进入待验证状态。
后台会将提交的证件做分类,再作为多任务,加入到异步队列中,再推给平台审核,从提交到到推审这个过程是异步的,推审到平台,这个过程也是异步的,平台回调,也是各个证件审查平台各自发起各自的回调,不会管其他证件平台是否回调,这个也是异步的。因为不可能等一类证件提交推审了,另外一类证件才能提交推审,也不是一类证件推给平台了,另外一类才能推,这样,任务之间就增加了不必要的耦合了,真正的处理就是,所有待审核证件绑定的子任务都各干各的。
同时,每个平台审核的时长不同,有的审的快,有的审查的慢,送去审查后,后台就将送审的任务挂起,等待平台回调,这个过程叫异步回调。
回调后,就涉及到了状态检查的设计逻辑。有的证件有结果,有的证件没结果,如何知道审查结束了没?可以就像controller中写的while逻辑一样,得等,得轮训检查,等所有子任务都isDone了,这时候才能算整个审核任务结束了。
但是这个做法,在这个案例中并不聪明。就这个案例来说,我们在真正设计业务的时候,没必要while轮询,只要在每类证件(每个子任务)推审平台之后,在数据库对子任务状态做标记,等平台异步回调的时候,修改该证件的标识位状态(从检查中到检查结束),再去检查每类证件的标识位是不是检查结束状态,就可以知道整个审核是否结束。