持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第30天,点击查看活动详情
一、DtpRegistry类
分析源码从DtpRegistry类开始,这个类主要功能是 注册、获取、刷新 某个动态线程池,某个动态线程池是DtpExecutor类,先看DtpRegistry类属性
再看其它方法,获取当前存在的线程池名字:
=== 拿到所有线程池的名字 ===
//获取Get all DtpExecutor names.
public static List<String> listAllDtpNames() {
return Lists.newArrayList(DTP_REGISTRY.keySet());
}
//获取Get all JUC ThreadPoolExecutor names.
public static List<String> listAllCommonNames() {
return Lists.newArrayList(COMMON_REGISTRY.keySet());
}
/**
* 注册线程池 a DtpExecutor.
*
* @param executor the newly created DtpExecutor instance
* @param source the source of the call to register method
*/
public static void registerDtp(DtpExecutor executor, String source) {
log.info("DynamicTp register dtpExecutor, source: {}, executor: {}",
source, ExecutorConverter.convert(executor));
DTP_REGISTRY.putIfAbsent(executor.getThreadPoolName(), executor);
}
/**
* 注册线程池 a common ThreadPoolExecutor.
*
* @param wrapper the newly created ThreadPoolExecutor wrapper instance
* @param source the source of the call to register method
*/
public static void registerCommon(ExecutorWrapper wrapper, String source) {
log.info("DynamicTp register commonExecutor, source: {}, name: {}", source, wrapper.getThreadPoolName());
COMMON_REGISTRY.putIfAbsent(wrapper.getThreadPoolName(), wrapper);
}
//根据name获取线程池
public static DtpExecutor getDtpExecutor(String name) {...}
public static ExecutorWrapper getCommonExecutor(String name) {...}
最后就是核心的动态刷新线程池入口的方法refresh:当注册中心的配置更新后,就refresh线程池
/**
* Refresh while the listening configuration changed.
*
* @param properties the main properties that maintain by config center
*/
public static void refresh(DtpProperties properties) {
// 判空
if (Objects.isNull(properties) || CollUtil.isEmpty(properties.getExecutors())) {
log.warn("DynamicTp refresh, empty threadPoolProperties.");
return;
}
properties.getExecutors().forEach(x -> {
if (StringUtils.isBlank(x.getThreadPoolName())) {
log.warn("DynamicTp refresh, threadPoolName must not be empty.");
return;
}
val dtpExecutor = DTP_REGISTRY.get(x.getThreadPoolName());
if (Objects.isNull(dtpExecutor)) {
log.warn("DynamicTp refresh, cannot find specified dtpExecutor, name: {}.", x.getThreadPoolName());
return;
}
// 刷新
refresh(dtpExecutor, x);
});
}
private static void refresh(DtpExecutor executor, ThreadPoolProperties properties) {
// 参数不合法校验
if (properties.getCorePoolSize() < 0 ||
properties.getMaximumPoolSize() <= 0 ||
properties.getMaximumPoolSize() < properties.getCorePoolSize() ||
properties.getKeepAliveTime() < 0) {
log.error("DynamicTp refresh, invalid parameters exist, properties: {}", properties);
return;
}
// 线程池旧配置
DtpMainProp oldProp = ExecutorConverter.convert(executor);
// 真正开始刷新
doRefresh(executor, properties);
// 线程池新配置
DtpMainProp newProp = ExecutorConverter.convert(executor);
// 打印日志
if (oldProp.equals(newProp)) {
log.warn("DynamicTp refresh, main properties of [{}] have not changed.", executor.getThreadPoolName());
return;
}
// 更新参数 日志打印
List<FieldInfo> diffFields = EQUATOR.getDiffFields(oldProp, newProp);
List<String> diffKeys = diffFields.stream().map(FieldInfo::getFieldName).collect(toList());
log.info("DynamicTp refresh, name: [{}], changed keys: {}, corePoolSize: [{}], maxPoolSize: [{}], " +
"queueType: [{}], queueCapacity: [{}], keepAliveTime: [{}], rejectedType: [{}], " +
"allowsCoreThreadTimeOut: [{}]",
executor.getThreadPoolName(),
diffKeys,
String.format(DynamicTpConst.PROPERTIES_CHANGE_SHOW_STYLE, oldProp.getCorePoolSize(), newProp.getCorePoolSize()),
String.format(DynamicTpConst.PROPERTIES_CHANGE_SHOW_STYLE, oldProp.getMaxPoolSize(), newProp.getMaxPoolSize()),
String.format(DynamicTpConst.PROPERTIES_CHANGE_SHOW_STYLE, oldProp.getQueueType(), newProp.getQueueType()),
String.format(DynamicTpConst.PROPERTIES_CHANGE_SHOW_STYLE, oldProp.getQueueCapacity(), newProp.getQueueCapacity()),
String.format("%ss => %ss", oldProp.getKeepAliveTime(), newProp.getKeepAliveTime()),
String.format(DynamicTpConst.PROPERTIES_CHANGE_SHOW_STYLE, oldProp.getRejectType(), newProp.getRejectType()),
String.format(DynamicTpConst.PROPERTIES_CHANGE_SHOW_STYLE, oldProp.isAllowCoreThreadTimeOut(),
newProp.isAllowCoreThreadTimeOut()));
val notifyItem = NotifyHelper.getNotifyItem(executor, NotifyTypeEnum.CHANGE);
// 平台提醒
boolean ifNotice = CollUtil.isNotEmpty(dtpProperties.getPlatforms()) &&
Objects.nonNull(notifyItem) &&
notifyItem.isEnabled();
if (!ifNotice) {
return;
}
// 上下文
DtpContext context = DtpContext.builder()
.dtpExecutor(executor)
.platforms(dtpProperties.getPlatforms())
.notifyItem(notifyItem)
.build();
DtpContextHolder.set(context);
// 用于提醒的线程池执行 提醒方法
NOTIFY_EXECUTOR.execute(() -> NotifierHandler.getInstance().sendNotice(oldProp, diffKeys));
}
真正的刷新方法 doRefresh,内部逻辑就是调用当前线程池的setXxx方法设置新的参数
private static void doRefresh(DtpExecutor dtpExecutor, ThreadPoolProperties properties) {
// 调用相应的setXxx方法更新线程池参数。
if (!Objects.equals(dtpExecutor.getCorePoolSize(), properties.getCorePoolSize())) {
dtpExecutor.setCorePoolSize(properties.getCorePoolSize());
}
if (!Objects.equals(dtpExecutor.getMaximumPoolSize(), properties.getMaximumPoolSize())) {
dtpExecutor.setMaximumPoolSize(properties.getMaximumPoolSize());
}
if (!Objects.equals(dtpExecutor.getKeepAliveTime(properties.getUnit()), properties.getKeepAliveTime())) {
dtpExecutor.setKeepAliveTime(properties.getKeepAliveTime(), properties.getUnit());
}
if (!Objects.equals(dtpExecutor.allowsCoreThreadTimeOut(), properties.isAllowCoreThreadTimeOut())) {
dtpExecutor.allowCoreThreadTimeOut(properties.isAllowCoreThreadTimeOut());
}
// update reject handler
if (!Objects.equals(dtpExecutor.getRejectHandlerName(), properties.getRejectedHandlerType())) {
dtpExecutor.setRejectedExecutionHandler(RejectHandlerGetter.getProxy(properties.getRejectedHandlerType()));
dtpExecutor.setRejectHandlerName(properties.getRejectedHandlerType());
}
// update work queue capacity
if (!Objects.equals(dtpExecutor.getQueueCapacity(), properties.getQueueCapacity()) &&
Objects.equals(properties.getQueueType(), VARIABLE_LINKED_BLOCKING_QUEUE.getName())) {
val blockingQueue = dtpExecutor.getQueue();
if (blockingQueue instanceof VariableLinkedBlockingQueue) {
((VariableLinkedBlockingQueue<Runnable>)blockingQueue).setCapacity(properties.getQueueCapacity());
} else {
log.error("DynamicTp refresh, the blockingqueue capacity cannot be reset, dtpName: {}, queueType {}",
dtpExecutor.getThreadPoolName(), dtpExecutor.getQueueName());
}
}
dtpExecutor.setWaitForTasksToCompleteOnShutdown(properties.isWaitForTasksToCompleteOnShutdown());
dtpExecutor.setAwaitTerminationSeconds(properties.getAwaitTerminationSeconds());
dtpExecutor.setPreStartAllCoreThreads(properties.isPreStartAllCoreThreads());
dtpExecutor.setRunTimeout(properties.getRunTimeout());
dtpExecutor.setQueueTimeout(properties.getQueueTimeout());
List<TaskWrapper> taskWrappers = TaskWrappers.getInstance().getByNames(properties.getTaskWrapperNames());
dtpExecutor.setTaskWrappers(taskWrappers);
if (CollUtil.isEmpty(properties.getNotifyItems())) {
properties.setNotifyItems(getDefaultNotifyItems());
}
// 更新提醒
NotifyHelper.updateNotifyItems(dtpExecutor, dtpProperties, properties);
}
二、DtpExecutor类——ThreadPoolExecutor的增强版儿子DtpExecutor
从继承链来看,DtpExecutor 间接实现了Executor接口,可以把DtpExecutor理解为加强版的儿子。
相比比ThreadPoolExecutor增加拓展了下面属性以及几个xxxTimeOut超时时间等
rejectCount:拒绝数量
rejectHandlerName:拒绝策略名称
notifyItems:需要提醒的平台
preStartAllCoreThreads:线程是否需要提前预热,真正调用的还是ThreadPoolExecutor的对应方法
public class DtpExecutor extends DtpLifecycleSupport {
/**
* Total reject count.
*/
private final AtomicInteger rejectCount = new AtomicInteger(0);
/**
* RejectHandler name.
*/
private String rejectHandlerName;
/**
* Notify items, see {@link NotifyTypeEnum}.
*/
private List<NotifyItem> notifyItems;
/**
* Task wrappers, do sth enhanced.
*/
private List<TaskWrapper> taskWrappers = Lists.newArrayList();
/**
* If pre start all core threads.
*/
private boolean preStartAllCoreThreads;
/**
* Task execute timeout, unit (ms), just for statistics.
*/
private long runTimeout;
/**
* Task queue wait timeout, unit (ms), just for statistics.
*/
private long queueTimeout;
/**
* Count run timeout tasks.
*/
private final AtomicInteger runTimeoutCount = new AtomicInteger();
/**
* Count queue wait timeout tasks.
*/
private final AtomicInteger queueTimeoutCount = new AtomicInteger();
...
...
}
三、DtpContext 类——充当dtp的上下文
该类充当dtp的上下文,类似ApplicationContext,将各个逻辑之间相关联。
@Builder @Data public class DtpContext {
// 增强的线程池儿子
private DtpExecutor dtpExecutor;
// 配置的告警通知的平台信列表
private List<NotifyPlatform> platforms;
// 具体通知项相关,内部包含需要通知的平台,包含字段
// List<String> platforms : 枚举NotifyPlatformEnum如钉钉、企微、email的,可自行拓展
// boolean enabled : 通知标志
// String type : 通知类型,枚举NotifyTypeEnum如change配置改变、liveness活跃度、reject拒绝、queue_timeout任务队列超时等
// int threshold;
// int interval = 120;
private NotifyItem notifyItem;
// 告警消息,包含
// type
// lastAlarmTime
// counter
private AlarmInfo alarmInfo;
// 获取某个平台
public NotifyPlatform getPlatform(String platform) {
if (CollUtil.isEmpty(platforms)) {
return null;
}
val map = platforms.stream()
.collect(toMap(x -> x.getPlatform().toLowerCase(), Function.identity(), (v1, v2) -> v2));
return map.get(platform.toLowerCase());
}
}
四、XxxConverter
该类主要是进行实体参数转化封装。
ExecutorConverter 将动态线程池 DtpExecutor 中的参数提取出来,然后再次封装进DtpMainProp实体,内部其实还是线程池的几大参数,DtpRegistry注册、刷新的动态线程池前后的时候会调用该转化方法
MetricsConnverter 将动态线程池 DtpExecutor 、ExecutorWrapper(ThreadPoolExecutor的在封装实体)中的参数提取出来,然后DtpEndpoint、DtpMonitor 类再次封装进ThreadPoolStats实体,访问暴露的Endponit拿到的信息就是ThreadPoolStats,记录监控日志的信息拿到的也是ThreadPoolStats。其中除了线程池核心的几个参数,还有多个总结参数如队列剩余容量,队列个数等,另外ThreadPoolStats extends Metries空类(中文意为指标)
五、DtpPostProcessor类——决定注册的时机
DtpPostProcessor类实现BeanPostProcessor接口,会在bean初始化的时候调用postProcessAfterInitialization,然后根据是否为动态线程池调响应的注册方法registerDtp、registerCommon,注册的动作就是向Map中put元素,key为线程池名字,value为线程池。
DtpPostProcessor类决定注册的时机,支持@Bean+ @EnableDynamicTp注解注册动态线程池和通过yml配置文件创建动态线程池。
1.spring容器启动时DtpPostProcessor会去注册在代码中通过@Bean声明的线程池实例 2.afterPropertiesSet方法会拉去配置中心配置的线程池然后实例化
六、DtpMonitor监控类
主要负责三种形式的监控方案,logging、jsonlog、micrometer,以及提供atactor暴露Endpoints来支持http访问动态拿到信息
实现ApplicationRunner类,具体的监控收集信息动作在run方法,委托doCollete遍历调用collete方法收集,最后还是调用的实现类collete方法。
七、AbstractNotifier、AlarmManager报警类
报警这块代码做了一些抽象设计,运用了像模板方法模式等,主要是集成企业微信、钉钉等第三方平台,动态监测参数达到阈值就报警;核心的接口Notifier以及抽象类AbstractNotifier是功能实现的模板,方便解耦扩展,第三方实现类只需要集成AbstractNotifier即可。
八、RejectedInvocationHandler拒绝类
在任务负载过重达到拒绝策略的阈值,会执行指定的拒绝策略,在拒绝策略执行之前,需要发送报警信息,实现原理就是动态代理,在实际拒绝策略方法之前加上发送报警的增强方法beforeReject(executor);完成具体的逻辑RejectAware接口内默认方法,然后就是 RejectedInvocationHandler implements InvocationHandler, RejectedAware 重写 invoke方法。