· Charlie · 工程技术 · 4 分钟阅读

SpringBoot中线程池使用

最近接手的项目遇到一个这样的问题:我们服务的一个接口需要循环查询另一个服务的接口,这样如果数据量较多的时候,我们服务的接口响应时间可能就会超时,伪代码如下: 这里先不讨论为什么不能让对方服务提供一个批量查询接口,然后通过…

SpringBoot中线程池使用

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

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

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

好吧,头疼医头吧。

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

优化步骤:

  1. 在项目中增加线程池配置,如下:
/**
 * @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 注解,如下:
/**
 * @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. 调用的时候
@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));

}
Share:
返回文章列表
Feign中传参使用的注解
工程技术

Feign中传参使用的注解

今天遇到一个需求,需要异步导出一些数据到文件中,因为导出的数据量比较大,所以接收到导出请求之后,就将需要导出的数据的ID写入了MQ中,消费端接收到MQ的消息之后,然后通过Feign调用其他的服务进行数据导出。 通过Fei…

领域驱动设计
工程技术

领域驱动设计

复杂软件设计的解决方案 DDD是Domain-Driven Design(领域驱动设计)的简称,它是一种解决复杂软件设计的方法论,它试图分离技术实现的复杂性,并围绕业务概念构建的领域模型,以解决软件难以理解,难以演进的问…

什么是CAP定理
工程技术

什么是CAP定理

什么是CAP定理? CAP定理告诉我们,一个分布式系统不可能同时满足一致性(C:Consistency),可用性(A: Availability)和分区容错性(P:Partition tolerance)这三个基本需求,…