最近接手的项目遇到一个这样的问题:我们服务的一个接口需要循环查询另一个服务的接口,这样如果数据量较多的时候,我们服务的接口响应时间可能就会超时,伪代码如下:

1
2
3
4
5
6
7
8
List<String> listOfResult = new ArrayList<>();
listOfData.stream().forEach(t -> {
// 调用其他微服务查询数据
String result = queryOtherServer(t);
if (null != result){
listOfResult.add(result);
}
});

这里先不讨论为什么不能让对方服务提供一个批量查询接口,然后通过一次调用获取数据结果,历史原因就是这样,短期只能我们系统先优化。

好吧,头疼医头吧。

这里如果要提高性能,最直接的办法就是并发查询,然后汇总查询结果。

优化步骤:

  1. 在项目中增加线程池配置,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* @description 自定义线程池配置
* @date 2021/7/3
*/
@Configuration
@EnableAsync
public class MyThreadPoolConfig {

/**
* 自定义线程池
* @return
*/
@Bean("myThreadPool")
public Executor threadPoolExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数:线程池创建时候初始化的线程数
executor.setCorePoolSize(10);
// 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(20);
// 缓冲队列:用来缓冲执行任务的队列
executor.setQueueCapacity(500);
// 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(60);
// 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix("do-something-");
// 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程)
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
executor.initialize();
return executor;
}
}

这里需要注意的是,一定要使用@EnableAsync

  1. 在需要异步执行的component方法上添加@Async 注解,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @author Charlie
* @description
* @date 2021/3/29
*/
@Component
public class TestComponent {

@Async("myThreadPool")
public CompletableFuture<String> mockHttpRequest(String requestId){
Long tId = Thread.currentThread().getId();
System.out.println("线程" + tId + "开始调用,请求ID为:" + requestId + " " + System.currentTimeMillis());
try {
Thread.sleep(new Random().nextInt(10) * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + tId + "调用结束,请求ID为:" + requestId + " " + System.currentTimeMillis());
return CompletableFuture.completedFuture(tId + ":" + requestId);
}
}

这里之所以返回CompletableFuture是因为需要接收返回值。

  1. 调用的时候
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@RequestMapping("/testThreadPool")
public void testThreadPool(){
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");

List<String> listOfResult = new ArrayList<>();
List<CompletableFuture<String>> listOfFuture = new ArrayList<>();
list.stream().forEach(t -> {
CompletableFuture<String> completableFuture = component.mockHttpRequest(t);
listOfFuture.add(completableFuture);
});

// 等待所有调用返回结果
CompletableFuture.allOf(listOfFuture.toArray(new CompletableFuture[listOfFuture.size()])).join();

listOfFuture.forEach(future -> {
try{
listOfResult.add(future.get());
}catch (Exception e){
// 异常处理
}
});

System.out.println(gson.toJson(listOfResult));

}