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轮询,只要在每类证件(每个子任务)推审平台之后,在数据库对子任务状态做标记,等平台异步回调的时候,修改该证件的标识位状态(从检查中到检查结束),再去检查每类证件的标识位是不是检查结束状态,就可以知道整个审核是否结束。