一、背景及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;@Beanpublic InitializeBean jadeInitBean() {InitializeBean initializeBean = new InitializeBean();// 注意这里,如果 uri 中 config 不是命名为 jade,则 name 属性需要设置为 jadeConfiguratorManager 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 {/*** 买药秒送频道线程池*/@Beanpublic 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);}