启用多线程调用接口

您所在的位置:网站首页 java怎么调用第三方接口 启用多线程调用接口

启用多线程调用接口

2023-07-30 04:56| 来源: 网络整理| 查看: 265

最近在做一个定时任务接口,接口的流程很简单,就是调用几个接口,然后将这这些接口的返回json字符串解析,存入后台数据库,编码很顺利,很快完成了。

随后负责人很快就发现问题,由于是单线程顺序调用,这些接口调用时间是线性叠加的,(假如前一个接口耗时10秒,在它结束前第二个接口一直处于阻塞中,这前一个接口调用所花费的时间就完全浪费了)我的这个模块响应时间已经超过了一分钟,于是提出了启用线程池的优化方案。

因为时间精力有限,暂时不研究线程池 阻塞队列这些,这里只提供一个简单易懂的解决方案

我很快选定了缓冲池作为线程池,具体创建过程代码如下

ExecutorService executorService = Executors.newCachedThreadPool();

第一次编码时,我用强大的百度查到了线程创建方法,也是我之前背诵的滚瓜烂熟的两种方式

extends Thread 或者 implements Runnable

当时我心想,这下我背的东西可是用得着了,于是我毫不犹豫,当场建了几个Thread类,用run方法包裹了自己的方法,一共5个接口,我建了5个Thread,分别包裹了这5个方法,迅速完成了5个线程,再依次调用线程池的submit方法依次传入这5个线程对象。

Thread thread1 = new Thread(new Runnable() { @Override public void run() { //封装的调用接口的方法, getCompareInfo(params); } });

Thread thread1 ...................................

executorService.submit(thread1); executorService.submit(thread2); executorService.submit(thread3); executorService.submit(thread4); executorService.submit(thread5);

代码很少,很快写完,让我顿时产生多线程不过如此的错觉,也就是创建一个线程然后用线程池去调度的事情,写完submit方法我觉得问题已经解决了,但是,当我想取回线程中调用接口的返回值然后解析的时候我意识到了一个尴尬的问题,Thread中的public void run()方法时没有返回值,这意味着我无法获取接口的返回。于是我思考了下,如果是启用线程去做写DB,或者存东西之类的操作我想用Thread是可行的,但是需要返回结果的时候就不行了

又是通过强大的专业广告搜索引擎,我百度倒了Callable这个类,并找到了应用的示例。

ExecutorService executorService = Executors.newCachedThreadPool(); CompletionService cs = new ExecutorCompletionService( executorService); Callable compareCallable = new Callable() { @Override public Map call() { return getCompareInfo(params); } }; cs.submit(compareCallable); cs.submit(*); cs.submit(*); cs.submit(*); cs.submit(*); Map res1 = cs.take().get(); Map res2 = cs.take().get(); Map res3 = cs.take().get(); Map res4 = cs.take().get(); Map res5 = cs.take().get();

我用Callable取代了Thread去执行接口调用方法,这里调度的对象换成了ExecutorCompletionService声明返回泛型是个map,最后按照调用顺序来获取接口返回

但随后的测试中我发现一个问题,我调用接口的顺序是

接口1

接口2

接口3

接口4

接口5

但是返回的顺序是1 2 5 3 4

因为接口响应有快有慢,比较快的接口先调用结束,就先返回了,顺序直接受到接口响应时间的影响,我还是无法拿到返回值,因为我不知道接口返回的顺寻,它是变动的

最后问了下业务熟悉的大佬,找到了FutureTask这个类,保留先前写好的Callable,要用到

//构建futureTask任务,控制返回顺序 FutureTask futureTask1 = new FutureTask(compareCallable); FutureTask futureTask2 = new FutureTask(getDeptInfoCallable); FutureTask futureTask3 = new FutureTask(getPaymentDetailInfoCallable); FutureTask futureTask4 = new FutureTask(getAllowanceDetailCallable);

直接把之前的callable对象作为参数传递到FutureTask的构造方法中,然后换成

ExecutorService executorService = Executors.newCachedThreadPool();

executorService的submit方法,调用

Map res = futureTask1.get();

Map res = futureTask2.get();

Map res = futureTask3.get();

Map res = futureTask4.get();

Map res = futureTask5.get();

这样就能按照顺序返回接口的响应了,在方法内我们可以用try catch包裹业务代码块处理异常,也可以用线程执行的返回状态判断接口调用是否正常,来设定重试机制,FutureTask的原理是阻塞队列,在任务结束之前会阻塞,所以取到的返回是有顺序的。

若是你的接口中存在多个调用三方接口的操作,或者多个大批量数据处理的工作,且它们之前是没有关联先后顺序的,你也可以像这样创建线程并启用线程池并行执行这些操作,我的接口在高早前响应时间是70秒,改造后10秒左右就可以结束,虽说不是很快,但相比之前已经强多了。

总结就是,使用Callable对象封装具体业务代码,然后依次塞进FutureTask,使用线程池调度,然后依次调用get方法取出返回值即可,这样就可以避免单线程代码中,后续业务因为前面的业务耗时长而造成的block(阻塞)带来的长响应时间。

转载请注明出处

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3