动态线程池的优化策略与实践

需求:在同一个页面下同时管理三家运营商(例如中国电信、中国联通、中国移动)的数据,涉及到调用三家不同的API接口,并在前端页面上统一展示和管理这些数据。
串行化查询:
执行过程如下,伪代码:

//电信
JSONObject result = restTemplateUtils.getHttp(telecomUrl, params, headersMap);
//联通
JSONObject result = restTemplateUtils.getHttp(unicomUrl, params, headersMap);
// 移动
JSONObject result = restTemplateUtils.getHttp(mobileUrl, params, headersMap);

串行化查询,即依次执行每个查询请求,这种做法在某些情况下是必要的,尤其是在不同请求间有依赖关系或资源限制的情况下。然而,当查询独立且可以并行执行时,串行查询存在一些明显的弊端,特别是在高并发和性能敏感的应用场景下。以下是串行化查询的一些主要弊端:

延迟增加:每次查询都需要等待前一个查询完成后才开始执行。这意味着总的响应时间等于各个查询响应时间之和,这可能导致用户感知到的延迟显著增加,尤其是在网络条件不佳或服务器响应慢的情况下。
吞吐量受限:由于每个请求必须等待前一个请求完成,系统的整体吞吐量(单位时间内处理的请求数量)受到限制。在高并发场景下,这可能导致系统无法充分利用其处理能力,从而影响整体性能。
资源浪费:在等待某个查询执行时,CPU和其他系统资源可能处于闲置状态,这降低了资源利用率。特别是在多核处理器环境中,串行执行无法利用多个核心并行处理的能力。
扩展性差:随着查询数量的增加,串行查询的总执行时间将线性增长,这使得系统很难通过简单增加硬件资源来提高性能。在需要处理大量数据或高并发请求的场景下,串行查询的扩展性成为一个瓶颈。

优化方案:并行执行

为了解决这些问题,通常推荐使用并行查询或异步查询的策略,通过使用线程池、异步I/O、并行流处理等技术,可以显著减少总响应时间和提高系统的吞吐量。

例如,在Java中,可以使用CompletableFuture API来并行执行多个HTTP请求,并通过CompletableFuture.allOf().get()来等待所有请求完成,这样可以有效地减少总延迟并提高资源利用率。

执行过程如下,伪代码:

//电信
 CompletableFuture<JSONObject> telecomFuture = CompletableFuture.supplyAsync(() -> {
    return restTemplateUtils.getHttp(telecomUrl, params, headersMap);
});

 //联通
CompletableFuture<JSONObject> unicomFuture = CompletableFuture.supplyAsync(() -> {
    return restTemplateUtils.getHttp(unicomUrl, params, headersMap);
});
                
 //移动
CompletableFuture<JSONObject> unicomFuture = CompletableFuture.supplyAsync(() -> {
    return restTemplateUtils.getHttp(mobileUrl, params, headersMap);
});        

 CompletableFuture.allOf(telecomFuture,unicomFuture,mobileFuture).get();

上面的方式并没有使用线程池,结合我们之前的文章,可以通过nacos实现一个动态线程池。
继续优化:使用动态线程池
基于 Nacos 配置的动态线程池管理功能,可以根据配置的变化来动态调整线程池的参数,同时监控线程池的状态并动态添加任务到线程池中。

核心代码如下:

import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.nacos.api.config.listener.Listener;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@RefreshScope
@Configuration
public class DynamicThreadPool implements InitializingBean {
    @Value("${core.size}")
    private String coreSize;

    @Value("${max.size}")
    private String maxSize;

    private static ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private NacosConfigManager nacosConfigManager;

    @Autowired
    private NacosConfigProperties nacosConfigProperties;

    @Override
    public void afterPropertiesSet() throws Exception {
        //按照nacos配置初始化线程池
        threadPoolExecutor = new ThreadPoolExecutor(Integer.parseInt(coreSize), Integer.parseInt(maxSize), 10L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(10),
                new ThreadFactoryBuilder().setNameFormat("c_t_%d").build(),
                new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        System.out.println("rejected!");
                    }
                });

        //nacos配置变更监听
        nacosConfigManager.getConfigService().addListener("service-dev.yml", nacosConfigProperties.getGroup(),
                new Listener() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }
                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        //配置变更,修改线程池配置
                        System.out.println(configInfo);
                        changeThreadPoolConfig(Integer.parseInt(coreSize), Integer.parseInt(maxSize));
                    }
                });
    }

    /**
     * 打印当前线程池的状态
     */
    public String printThreadPoolStatus() {
        return String.format("core_size:%s,thread_current_size:%s;" +
                        "thread_max_size:%s;queue_current_size:%s,total_task_count:%s", threadPoolExecutor.getCorePoolSize(),
                threadPoolExecutor.getActiveCount(), threadPoolExecutor.getMaximumPoolSize(), threadPoolExecutor.getQueue().size(),
                threadPoolExecutor.getTaskCount());
    }

    /**
     * 给线程池增加任务
     *
     * @param count
     */
    public void dynamicThreadPoolAddTask(int count) {
        for (int i = 0; i < count; i++) {
            int finalI = i;
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(finalI);
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    /**
     * 修改线程池核心参数
     *
     * @param coreSize
     * @param maxSize
     */
    private void changeThreadPoolConfig(int coreSize, int maxSize) {
        threadPoolExecutor.setCorePoolSize(coreSize);
        threadPoolExecutor.setMaximumPoolSize(maxSize);
    }
}

伪代码:

 //电信
 CompletableFuture<JSONObject> telecomFuture = CompletableFuture.supplyAsync(() -> {
    return restTemplateUtils.getHttp(telecomUrl, params, headersMap);
}, DynamicThreadPool.threadPoolExecutor);

 //联通
CompletableFuture<JSONObject> unicomFuture = CompletableFuture.supplyAsync(() -> {
    return restTemplateUtils.getHttp(unicomUrl, params, headersMap);
}, DynamicThreadPool.threadPoolExecutor);
                
 //移动
CompletableFuture<JSONObject> unicomFuture = CompletableFuture.supplyAsync(() -> {
    return restTemplateUtils.getHttp(mobileUrl, params, headersMap);
}, DynamicThreadPool.threadPoolExecutor);        

 CompletableFuture.allOf(telecomFuture,unicomFuture,mobileFuture).get();

至于线程池参数设置多少合适呢?对于IO密集型任务和计算密集型任务,线程池的设置略有不同:

IO密集型任务:

对于IO密集型任务,通常建议设置较大的线程池大小,以便充分利用CPU等资源,同时能够处理大量的IO操作。

可以考虑设置线程池大小为2 * CPU核心数或更大,这样可以充分利用系统资源并提高IO操作的并发处理能力。

计算密集型任务:

对于计算密集型任务,由于任务主要耗费在CPU计算上,因此需要限制线程池的大小,避免过多线程竞争CPU资源而导致性能下降。

建议将线程池的大小设置为CPU核心数加1或2,这样可以充分利用CPU资源而又不至于引起过多的线程切换导致性能损失。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

princeAladdin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值