页面查询多项数据组合的线程池设计 | 京东云技术团队

news2024/12/22 19:20:54

背景

我们应对并发场景时一般会采用下面方式去预估线程池的线程数量,比如QPS需求是1000,平均每个任务需要执行的时间是t秒,那么我们需要的线程数是t * 1000。

但是在一些情况下,这个t是不好估算的,即便是估算出来了,在实际的线程环境上也需要进行验证和微调。比如在本文所阐述分页查询的数据项组合场景中。

1、数据组合依赖不同的上游接接口, 它们的响应时间参差不齐,甚至差距还非常大。有些接口支持批量查询而另一些则不支持批量查询。有些接口因为性能问题还需要考虑降级和平滑方案。

2、为了提升用户体验,这里的查询设计了动态列,因此每一次访问所需要组合的数据项和数量也是不同的。

因此这里如果需要估算出一个合理的t是不太现实的。

方案

一种可动态调节的策略,根据监控的反馈对线程池进行微调。整体设计分为装配逻辑线程池封装设计。

1、装配逻辑

查询结果,拆分分片(水平拆分),并行装配(垂直拆分),获得装配项列表(动态列), 并行装配每一项。

2、线程池封装

可调节的核心线程数、最大线程数、线程保持时间,队列大小,提交任务重试等待时间,提交任务重试次数。 固定异常拒绝策略。

调节参数:

字段名称说明
corePoolSize核心线程数参考线程池定义
maximumPoolSize最大线程数参考线程池定义
keepAliveTime线程存活时间参考线程池定义
queueSize队列长度参考线程池定义
resubmitSleepMillis提交任务重试等待时间添加任务被拒绝后重试时的等待时间
resubmitTimes提交任务重试次数添加任务被拒绝后重试添加的最大次数
    @Data
	private static class PoolPolicy {

		/** 核心线程数 */
		private Integer corePoolSize;

		/** 最大线程数 */
		private Integer maximumPoolSize;

		/** 线程存活时间 */
		private Integer keepAliveTime;

		/** 队列容量 */
		private Integer queueSize;

		/** 重试等待时间 */
		private Long resubmitSleepMillis;

		/** 重试次数 */
		private Integer resubmitTimes;
	}

创建线程池:

线程池的创建考虑了动态的需求,满足根据压测结果进行微调的要求。首先缓存旧的线程池后再创建新的线程,当新的线程池创建成功后再去关闭旧的线程池。保证在这个替换过程中不影响正在执行的业务。线程池使用了中断策略,用户可以及时感知到系统繁忙并保证了系统资源占用的安全。

public void reloadThreadPool(PoolPolicy poolPolicy) {
    if (poolPolicy == null) {
        throw new RuntimeException("The thread pool policy cannot be empty.");
    }
    if (poolPolicy.getCorePoolSize() == null) {
        poolPolicy.setCorePoolSize(0);
    }
    if (poolPolicy.getMaximumPoolSize() == null) {
        poolPolicy.setMaximumPoolSize(Runtime.getRuntime().availableProcessors() + 1);
    }
    if (poolPolicy.getKeepAliveTime() == null) {
        poolPolicy.setKeepAliveTime(60);
    }
    if (poolPolicy.getQueueSize() == null) {
        poolPolicy.setQueueSize(Runtime.getRuntime().availableProcessors() + 1);
    }
    if (poolPolicy.getResubmitSleepMillis() == null) {
        poolPolicy.setResubmitSleepMillis(200L);
    }
    if (poolPolicy.getResubmitTimes() == null) {
        poolPolicy.setResubmitTimes(5);
    }
    // - 线程池策略没有变化直接返回已有线程池。
    ExecutorService original = this.executorService;
    this.executorService = new ThreadPoolExecutor(
            poolPolicy.getCorePoolSize(),
            poolPolicy.getMaximumPoolSize(),
            poolPolicy.getKeepAliveTime(), TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(poolPolicy.getQueueSize()),
            new ThreadFactoryBuilder().setNameFormat(threadNamePrefix + "-%d").setDaemon(true).build(),
            new ThreadPoolExecutor.AbortPolicy());
    this.poolPolicy = poolPolicy;
    if (original != null) {
        original.shutdownNow();
    }
}

任务提交:

线程池封装对象中使用的线程池拒绝策略是AbortPolicy,因此在线程数和阻塞队列到达上限后会触发异常。另外在这里为了保证提交的成功率利用重试策略实现了一定程度的延迟处理,具体场景中可以结合业务特点进行适当的调节和配置。

public <T> Future<T> submit(Callable<T> task) {
    RejectedExecutionException exception = null;
    Future<T> future = null;
    for (int i = 0; i < this.poolPolicy.getResubmitTimes(); i++) {
        try {
            // - 添加任务
            future = this.executorService.submit(task);
            exception = null;
            break;
        } catch (RejectedExecutionException e) {
            exception = e;
            this.theadSleep(this.poolPolicy.getResubmitSleepMillis());
        }
    }
    if (exception != null) {
        throw exception;
    }
    return future;
}

监控:

1、submit提交的监控

见代码中的「监控点①」,在submit方法中添加监控点,监控key的需要添线程池封装对象的线程名称前缀,用于区分具体的线程池对象。

「监控点①」用于监控添加任务的动作是否正常,以便对线程池对象及策略参数进行微调。

public <T> Future<T> submit(Callable<T> task) {
    // - 监控点①
    CallerInfo callerInfo = Profiler.registerInfo(UmpConstant.THREAD_POOL_WAP + threadNamePrefix,
                UmpConstant.APP_NAME,
                UmpConstant.UMP_DISABLE_HEART,
                UmpConstant.UMP_ENABLE_TP);
    RejectedExecutionException exception = null;
    Future<T> future = null;
    for (int i = 0; i < this.poolPolicy.getResubmitTimes(); i++) {
        try {
            // - 添加任务
            future = this.executorService.submit(task);
            exception = null;
            break;
        } catch (RejectedExecutionException e) {
            exception = e;
            this.theadSleep(this.poolPolicy.getResubmitSleepMillis());
        }
    }
    if (exception != null) {
        // - 监控点①
        Profiler.functionError(callerInfo);
        throw exception;
    }
    // - 监控点①
    Profiler.registerInfoEnd(callerInfo);
    return future;
}

2、线程池并行任务

见代码的「监控点②」,分别在添加任务和任务完成后。

「监控点②」实时统计在线程中执行的总任务数量,用于评估线程池的任务的数量的满载水平。

/** 任务并行数量统计 */
private AtomicInteger parallelTaskCount = new AtomicInteger(0);

public <T> Future<T> submit(Callable<T> task) {
    RejectedExecutionException exception = null;
    Future<T> future = null;
    for (int i = 0; i < this.poolPolicy.getResubmitTimes(); i++) {
        try {
            // - 添加任务
            future = this.executorService.submit(()-> {
                T rst = task.call();
                // - 监控点②
                log.info("{} - Parallel task count {}", this.threadNamePrefix,  this.parallelTaskCount.decrementAndGet());
                return rst;
            });
            // - 监控点②
            log.info("{} + Parallel task count {}", this.threadNamePrefix,  this.parallelTaskCount.incrementAndGet());
            exception = null;
            break;
        } catch (RejectedExecutionException e) {
            exception = e;
            this.theadSleep(this.poolPolicy.getResubmitSleepMillis());
        }
    }
    if (exception != null) {
        throw exception;
    }
    return future;
}

3、调节

线程池封装对象策略的调节时机

1)上线前基于流量预估的压测阶段;

2)上线后跟进监控数据和线程池中任务的满载水平进行人工微调,也可以通过JOB在指定的时间自动调整;

3)大促前依据往期大促峰值来调高相关参数。

线程池封装对象策略的调节经验

1)访问时长要求较低时,我们可以考虑调小线程数和阻塞队列,适当调大提交任务重试等待时间和次数,以便降低资源占用。

2)访问时长要求较高时,就需要调大线程数并保证相对较小的阻塞队列,调小提交任务的重试等待时间和次数甚至分别调成0和1(即关闭重试提交逻辑)。

作者:京东零售 王文明

来源:京东云开发者社区 转载请注明来源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1113845.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据仓库分层

原因 用空间换时间&#xff0c;通过大量的预处理来提升应用系统的用户体验&#xff08;效率&#xff09;&#xff0c;因此数据仓库会存在大量冗余的数据。如果不分层的话&#xff0c;如果源业务系统的业务规则发生变化将会影响整个数据清洗过程&#xff0c;工作量巨大。通过数…

C# Onnx Yolov8 Detect 烟雾检测

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;namespace Onnx…

MAX插件CG Magic怎么云渲染?操作方法已整起!

小编这里会收到不少网友的反馈是关于3ds max插件CG Magic怎么云渲染&#xff1f; 3d max的这款插件CG MAGIC的出现就是为了设计师使用过程中&#xff0c;可以省时又省心的完成工作。 同时&#xff0c;大家要了键下&#xff0c;现阶段CG MAGIC有18个板块&#xff0c;118个模块…

安装Git和git命令使用

文章目录 安装Git创建版本库版本回退工作区和暂存区管理修改撤销修改 安装Git 在Windows上安装Git 在Windows上使用Git&#xff0c;可以从Git官网直接下载安装程序&#xff0c;然后按默认选项安装即可。 安装完成后&#xff0c;在开始菜单里找到“Git”->“Git Bash”&…

三星正在开发HBM4,预计2025年推出

近年来&#xff0c;人工智能&#xff08;AI&#xff09;、高性能计算&#xff08;HPC&#xff09;和PC一直在推动高性能DRAM产品的研发&#xff0c;市场对HBM类DRAM的需求也在迅速增长&#xff0c;各大厂商也加大了这方面的投入。目前HBM市场主要由三星、SK海力士和美光三家存储…

海尔智家亮相广交会:用确定的用户思维,战胜不确定的市场

全球家电市场的不确定性越来越强。 尽管家电行业复苏趋势不改&#xff0c;但是新的问题已显现。据中国机电产品进出口商会数据&#xff0c;今年1-8月&#xff0c;中国白色家电出口额同比下降2%。同时&#xff0c;据媒体报道&#xff0c;在近日举办的中国进出口贸易交易会&…

【红日靶场】vulnstack5-完整渗透过程

系列文章目录 【红日靶场】vulnstack1-完整渗透过程 【红日靶场】vulnstack2-完整渗透过程 【红日靶场】vulnstack3-完整渗透过程 【红日靶场】vulnstack4-完整渗透过程 文章目录 系列文章目录描述虚拟机密码红队思路 一、环境初始化二、开始渗透外网打点上线cs权限提升域信息…

重置手机网络虽然麻烦,但效果杠杠的!如何重置安卓手机的网络

在这篇文章中&#xff0c;我们将探讨你可能需要在Android设备上重置网络设置的原因&#xff0c;并将提供如何重置的分步说明。无论你是遇到连接问题&#xff0c;还是只是想重新开始网络设置&#xff0c;本指南都将引导你完成重置过程。 重置网络设置的原因 在Android设备上重置…

电流监测芯片SGM8199A2应用电路设计

SGM8199是一系列具有电压输出功能的双向电流监测芯片&#xff0c;用于监测共模电压范围内分流电阻上的压降&#xff0c;而不受电源电压的影响。该器件具有-0.1V至26V的宽共模电压范围输入。低偏移使得在监测电流时允许分流器上的满量程最大压降为10mV。SGM8199系列提供三种固定…

高速DSP系统设计参考指南(五)印制电路板或PCB布局

&#xff08;五&#xff09;印制电路板或PCB布局 所有电路设计完成后&#xff0c;下一步是电路板布局。这是开发过程中非常关键的一步&#xff0c;因为滤波电路的有效性取决于元件相对于 DSP引脚的放置位置。此外&#xff0c;电路板布局对噪声、串扰和传输线效应有很大影响&…

杭州怎么开股票账户佣金手续费最低?找哪家证券公司?

杭州怎么开股票账户佣金手续费最低&#xff1f;找哪家证券公司&#xff1f; 股票开户是指一个人或实体在证券公司或证券交易所注册并开立证券账户&#xff0c;以便购买和出售股票。股票开户需要提供身份证明文件、联系方式、银行账号等信息&#xff0c;并接受有关监管机构的审…

400 The plain HTTP request was sent to HTTPS port

接口请求发生问题&#xff1a; 解决方法&#xff1a; Nginx HTTP服务器的报错 “400 Bad Request: The plain HTTP request was sent to HTTPS port”&#xff0c;本文将讲解如何解决这个问题。简单从报错的字面意思上来看&#xff0c;是因为HTTP请求被发送到HTTPS端口&#x…

2022年京东双11美妆护肤品类数据回顾

在美妆护肤市场中&#xff0c;回望去年双11&#xff0c;虽然期间品牌在市场中的销售表现有升有降&#xff0c;但根据京东官方发布的数据来看&#xff0c;不少美妆品牌仍在京东双11期间取得了亮眼成绩&#xff0c;其中&#xff0c;赫莲娜等952个品牌成交额同比增长超100%。 下面…

基于Django与深度学习的股票预测系统 计算机竞赛

文章目录 0 前言1 课题背景2 实现效果3 Django框架4 数据整理5 模型准备和训练6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于Django与深度学习的股票预测系统 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff…

食品添加剂:健康还是危险?——从肠道微生物角度分析

谷禾健康 就在前不久&#xff0c;世界卫生组织将阿斯巴甜列为可能致癌物&#xff0c;引发了人们不小的热议。阿斯巴甜作为人工甜味剂的一种&#xff0c;不提供任何卡路里&#xff0c;就可以提供蔗糖几百倍的甜度。 人工甜味剂几乎不提供热量&#xff0c;或只提供极少的热量&…

最新最全计算机专业毕业设计选题精华汇总-持续更新中

文章目录 0 前言1 计算机毕设选题推荐2 开题指导3 最后 0 前言 大家好&#xff01;大四的同学们毕业设计即将开始了&#xff0c;你们做好准备了吗&#xff1f; 学长给大家精心整理了最新的计算机毕业设计选题&#xff0c;希望能为你们提供帮助。如果在选题过程中有任何疑问&a…

Spring篇---第六篇

系列文章目录 文章目录 系列文章目录一、Spring 框架中的单例 Bean 是线程安全的么?二、Spring 是怎么解决循环依赖的?三、说说事务的隔离级别一、Spring 框架中的单例 Bean 是线程安全的么? Spring 框架并没有对单例 Bean 进行任何多线程的封装处理。 关于单例 Bean 的线程…

【广州华锐互动】利用VR开展建筑塔吊安全操作学习的好处?

随着科技的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术已经逐渐渗透到各个领域&#xff0c;为人们的生活带来了前所未有的便利。在工程教育领域&#xff0c;VR建筑塔吊安全操作学习作为一种新型的教学手段&#xff0c;正逐渐成为提高教学质量和培养高素质工程…

《深入理解java虚拟机 第三版》学习笔记二

第 4 章 虚拟机性能监控、故障处理工具 4.2 基础故障处理工具 4.2.1 jps&#xff1a;虚拟机进程状况工具 可以列出正在运行的虚拟机进程&#xff0c;并显示虚拟机执行主类&#xff08;Main Class&#xff0c;main()函数所在的类&#xff09;名称以及这些进程的本地虚拟机唯一…

36v转变5V5A车充降压芯片

需要一款将36V降压为5V 5A的车载充电器降压芯片。 根据你的需求&#xff0c;推荐使用AH1514芯片作为替代选项。AH1514芯片具有广泛的输入电压范围&#xff08;7V至38V&#xff09;和20A的峰值输出电流&#xff0c;适合车载电源应用。它采用SSOP10封装&#xff0c;具备外置MOS管…