买药秒送 JADE动态线程池实践及原理浅析
一、背景及JADE介绍
买药秒送是健康即时零售业务新的核心流量场域,面对京东首页高流量曝光,我们对频道页整个技术架构方案进行升级,保障接口高性能、系统高可用。
动态线程池是买药频道应用的技术之一,我们通过3轮高保真压测最终初步确定了线程池的核心参数。但我们仍面临一些保障系统稳定性问题:如何监控线程池运行状态?以及因流量飙升出现任务堆积和拒绝时能否实时报警,线程池核心参数能否做到不重启应用,动态调整即时生效?
经调研,业界成熟的动态线程池开源项目有 dynamic-tp 和 hippo4j,在京东内部应用比较广泛的方案是 JADE ,几种方案实现思路大致相同,感兴趣可自行了解。JADE 是由零售中台-研发架构组维护的项目,动态线程池是JADE的组件之一,其稳定性已得到广泛验证(集团应用 300+,零售交易服务中台应用 250+ ,其中 0 级应用 130+),与JADE相辅相成的还有万象平台:是可视化的JADE管理端,集成配置、监控、审批等能力的JADE可视化平台,可以更高效的使用JADE组件,进一步提高工作效率。
实现效果
接入JADE和万象后,买药秒送线程池秒级监控效果如下:实时监控线程池运行状态 以及 阈值报警。
下面我们从实践到原理一探究竟。
二、JADE动态线程池+万象可视化平台接入实践
JADE动态线程池和万象整体流程图如下:应用中需要引入 JADE、DUCC和 PFinder SDK,通过JADE创建线程池,线程池核心参数通过万象平台配置,集成 DUCC 实现动态调参,即时生效。线程池运行状态监控通过 PFinder 实现秒级监控。
1、引入JADE POM依赖,jade从1.2.4版本开始支持万象
<!-- JADE 核心包,最新版本与发布说明-->
<dependency>
<groupId>com.jd.jade</groupId>
<artifactId>jade</artifactId>
<version>1.2.4</version>
</dependency>
<!-- 引用 PFinder SDK 库 -->
<dependency>
<groupId>com.jd.pfinder</groupId>
<artifactId>pfinder-profiler-sdk</artifactId>
<version>1.1.5-FINAL</version>
</dependency>
<!-- JADE 配置目前基于 XStream 进行 XML 格式序列化,若通过非 DBConfig 动态调参,需自行引入 -->
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.19</version>
</dependency>
<!-- JADE 尚未完全解除 dbconfig 依赖(主要是 ConfigBase 基类及 XmlConfigService 接口),非交易应用,需自行引入 dbconfig-client-api 精简包,交易应用一般已直接引用 dbconfig-client 实现包 -->
<dependency>
<groupId>com.jd.purchase.config</groupId>
<artifactId>dbconfig-client-api</artifactId>
<version>1.0.8</version>
</dependency>
2、创建 jade.properties
配置文件,并通过 Spring
加载该配置文件。
# 万象平台环境配置
jade.wx.env=pre
# 以下为调试设置,线上环境无需配置
jade.log.level=debug
jade.meter.debug-enabled=true
Spring
加载 JADE
配置文件<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!--JADE配置-->
<value>classpath:jade.properties</value>
</list>
</property>
<property name="fileEncoding">
<value>UTF-8</value>
</property>
</bean>
3、配置JADE启动类,负责 JADE 自定义初始化 。
JDOS
应用默认创建一个DUCC
空间,使用万象的DUCC
进行配置和更新。/**
* @description:JADE配置类
* @author: rongtao7
* @date: 2024/4/5 1:09 下午
*/
@Configuration
public class JadeConfig {
@Value("ucc://${ducc.application}:${ducc.token}@${ducc.hostPort}/v1/namespace/${ducc.namespace}/config/${ducc.config}/profiles/${ducc.profile}?longPolling=15000")
private String duccUrl;
@Value("${jade.wx.env}")
private String wxEnv;
@Bean
public InitializeBean jadeInitBean() {
InitializeBean initializeBean = new InitializeBean();
// 注意这里,如果 uri 中 config 不是命名为 jade,则 name 属性需要设置为 jade
ConfiguratorManager instance = new ConfiguratorManager();
instance.addResource("jade", duccUrl);
initializeBean.setConfigServiceProvider(instance);
// 万象环境
initializeBean.setWxEnv(wxEnv);
return initializeBean;
}
}
4、使用JADE
创建线程池,并通过PFinder
包装增强以支持trace
的传递
prestart()
用于预热核心线程/**
* 线程池配置类,集成JADE和万象平台
*/
@Configuration
public class TaskExecutePoolConfig {
/**
* 买药秒送频道线程池
*/
@Bean
public ExecutorService msChannelPagePool(){
//JADE组件创建线程池
ThreadPoolExecutor threadPoolExecutor = ThreadPoolExecutorBuilder.newBuilder()
.name(ThreadPoolName.MS_CHANNEL_PAGE_POOL.name()) // 线程池名称
.core(200) // 核心线程数
.max(200) // 最大线程数
.queue(100) // 设置队列长度,此队列支持动态调整
.callerRuns() // 拒绝策略,内置监控、日志
.keepAliveTime(60L, TimeUnit.SECONDS) //线程存活时间
.prestart() // 预初始化所有核心线程数
.build();
// Pfinder增强
return PfinderContext.executorServiceWrapper(threadPoolExecutor);
}