页面查询多项数据组合的线程池设计 | 京东云技术团队
背景
我们应对并发场景时一般会采用下面方式去预估线程池的线程数量,比如QPS需求是1000,平均每个任务需要执行的时间是t秒,那么我们需要的线程数是t * 1000。
但是在一些情况下,这个t是不好估算的,即便是估算出来了,在实际的线程环境上也需要进行验证和微调。比如在本文所阐述分页查询的数据项组合场景中。
1、数据组合依赖不同的上游接接口, 它们的响应时间参差不齐,甚至差距还非常大。有些接口支持批量查询而另一些则不支持批量查询。有些接口因为性能问题还需要考虑降级和平滑方案。
2、为了提升用户体验,这里的查询设计了动态列,因此每一次访问所需要组合的数据项和数量也是不同的。
因此这里如果需要估算出一个合理的t是不太现实的。
方案
一种可动态调节的策略,根据监控的反馈对线程池进行微调。整体设计分为装配逻辑和线程池封装设计。
1、装配逻辑
查询结果,拆分分片(水平拆分),并行装配(垂直拆分),获得装配项列表(动态列), 并行装配每一项。

2、线程池封装
可调节的核心线程数、最大线程数、线程保持时间,队列大小,提交任务重试等待时间,提交任务重试次数。 固定异常拒绝策略。
调节参数:
| 字段 | 名称 | 说明 |
|---|---|---|
| corePoolSize | 核心线程数 | 参考线程池定义 |
| maximumPoolSize | 最大线程数 | 参考线程池定义 |
| keepAliveTime | 线程存活时间 | 参考线程池定义 |
| queueSize | 队列长度 | 参考线程池定义 |
| resubmitSleepMillis | 提交任务重试等待时间 | 添加任务被拒绝后重试时的等待时间 |
| resubmitTimes | 提交任务重试次数 | 添加任务被拒绝后重试添加的最大次数 |
@Dataprivate 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(即关闭重试提交逻辑)。
作者:京东零售 王文明
来源:京东云开发者社区 转载请注明来源
相关文章:
页面查询多项数据组合的线程池设计 | 京东云技术团队
背景 我们应对并发场景时一般会采用下面方式去预估线程池的线程数量,比如QPS需求是1000,平均每个任务需要执行的时间是t秒,那么我们需要的线程数是t * 1000。 但是在一些情况下,这个t是不好估算的,即便是估算出来了&…...
设计模式:策略模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)
简介: 策略模式,它是一种行为型设计模式,它定义了算法族,分别封装起来,让它们之间可以互相替换。策略模式让算法的变化独立于使用算法的客户,降低了耦合,增加了系统的可维护性和可扩展性。 策…...
详解如何快速查询Node.js版本信息(六种方法)
Node.js是一款基于Chrome V8引擎的快速、轻量级的JavaScript运行时。随着应用程序规模越来越庞大,Node.js版本的更新也日益频繁。这篇文章旨在帮助开发者们快速查询Node.js版本信息。 一、使用node命令查询版本信息 node命令提供了一个参数-v,可以在控…...
YOLOV8目标检测——最全最完整模型训练过程记录
文章目录 前言1 下载yolov8([网址](https://github.com/ultralytics/ultralytics))2 配置conda环境3 用pycharm打开文件3 训练自己的YOLOV8数据集4 run下运行完了之后没有best.pt文件5 导出为onnx文件6 yolov8应用完整案例(免费且包含源代码、…...
【C语言】用函数实现模块化程序设计
前言:如果把所有的程序代码都写在一个主函数(main函数)中,就会使主函数变得庞杂、头绪不清,使阅读和维护程序变得困难。此外,有时程序中要多次实现某一功能,如果重新编写实现此功能就会使得程序冗长、不精炼。 &#x…...
忆联SR-IOV解决方案:助力云数据中心节能提效,向“绿”而行
随着AI时代的到来,云数据中心如何实现节能提效正成为热门话题。其中,SR-IOV技术凭借灵活度高以及可节约虚拟化业务算力等优势,是打造绿色低碳云数据中心的重要解决方案之一。 一、什么是SR-IOV 技术 SR-IOV 是由国际组织 PCI-SIG 组织定义的…...
LeetCode:1402. 做菜顺序、2316. 统计无向图中无法互相到达点对数
1. 1402 做菜顺序 题目详细为: 一个厨师收集了他 n 道菜的满意程度 satisfaction ,这个厨师做出每道菜的时间都是 1 单位时间。 一道菜的 「 like-time 系数 」定义为烹饪这道菜结束的时间(包含之前每道菜所花费的时间)乘以这道菜…...
【消费战略】解读100个食品品牌|意面突起,“空刻”的品类心智占位!
空刻意面,一个开创意大利面速食化的新消费品牌,凭借着核心大单品意大利面,在过去短短的几年中,获得不俗的市场成绩和品牌影响力,占领了空刻意面的消费心智: 2019年,AIRMETER氢刻意面上线天猫旗舰…...
地图金字塔所在块的经纬度方位
地图金字塔所在块的经纬度方位 算法 #define LON_SPAN 360.0 // 开始经度(最左端) #define LAT_SPAN 180.0 #define GLOBAL_LEFT -180.0 // 开始纬度(最上端) #define GLOBAL_TOP 90.0 #define GLOBAL_RIGHT 180.0 #define GLOBAL_BOTTOM -90.0 // 地球的纬度跨度(180-(-180))…...
【干货】Java函数式编程公式大全,收藏学习!
函数操作是现代编程领域中的核心概念之一,它以类似 Excel 表格的方式进行数据处理和计算。它的特点是使用公式和函数来描述数据之间的关系和计算逻辑;它允许我们以更高效、更有组织的方式管理和处理数据。 在函数式编程中,数据被组织成表格的…...
django基于Python的房价预测系统+爬虫+大屏可视化分析
欢迎大家点赞、收藏、关注、评论 文章目录 前言一、项目介绍二、开发环境三、功能需求分析1 数据采集功能设计2数据管理功能设计3爬虫功能需求分析4 数据可视化功能需求分析数据库表的设计 四、核心代码五、效果图六、文章目录 前言 房价是一个国家经济水平的重要体现ÿ…...
异地组网企业怎么办理手续?
对于那些具有异地分支机构的企业来说,SDWAN(Software Defined Wide Area Network)可以是 提供高性能通信和数据传输的理想解决方案。那么,对于企业来说,SDWAN异地组网需要办理哪 些手续呢?下面将介绍一些关键的办理步骤。 1. 资质准备&…...
Android 13.0 根据包名授予OP_REQUEST_INSTALL_PACKAGES权限
1.概述 在系统13.0的定制化开发中,对于在app中调用安装第三方app的时候,会在这时弹出安装未知来源弹窗,需要默认授予REQUEST_INSTALL_PACKAGES 权限,来安装第三方app的安装未知来源权限,所以就是今天需要解决的这个问题 2.根据包名授予OP_REQUEST_INSTALL_PACKAGES的核心…...
民安智库(湖北知名满意度测评公司)乘客高铁出行调查:从需求到满意
随着科技的飞速发展,高铁已成为我们日常出行的重要选择。然而,什么样的服务才是乘客真正需要的?什么样的调查才能真实反映乘客的感受?民安智库(政务服务第三方评估公司)作为一家中国独立第三方调研咨询的公…...
Oracle的dbms.rls实现数据访问控制
在大部份系统中,权限控制主要定义为模块进入权限的控制和数据列访问权限的控制(如:某某人可以进入某个控制,仓库不充许查看有关部门的字段等等)。 但在某些系统中,权限控制又必须定义到数据行访问权限的控制,此需求一般…...
Python 自定义函数的基本步骤
一、Python 自定义函数的基本步骤 1、什么是函数 函数,其实我们一开始学 Python 的时候就接触过。 不过我们使用的大多数都是 Python 的内置函数。 比如基本每个章节都会出现的 print() 函数。 而现在,我们主要学习的是自定义函数。 各位有没有想过…...
阿里云新品云服务器实例,经济型e实例,价格便宜,性价比高
前不久,阿里云推出了一款全新云服务器实例,他是阿里云面向个人开发者、学生、小微企业,在中小型网站建设、开发测试、轻量级应用等场景推出的全新入门级云服务器,基于“飞天CIPU”黄金技术架构设计,可轻松满足网站建设…...
统信操作系统UOS上安装arm64版nginx
原文链接:统信操作系统UOS上安装arm64版nginx hello,大家好啊,今天给大家带来一篇在统信桌面操作系统UOS上安装arm64版nginx的文章,本篇文章主要是给大家提供一种下载离线nginx软件包的方法,拿到软件包可以去不能链接互…...
2017年高热度编程语言简介
世上语言千千万,我却独爱这一种!”这句话用来形容程序员和编程语言之间的爱恨情仇实在是再精准不过了。根据GitHub 2016年的开源报告,其上所有开源项目共包含了316种编程语言,这是一个什么概念呢?举个例子来说,世界上共有226个国…...
python爬虫入门(一)web基础
HTTP基本要点 HTTP请求,由客户端向服务端发出,可以分为 4 部分内容:请求方法(Request Method)、请求的网址(Request URL)、请求头(Request Headers)、请求体(…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
