动态线程池dynamic-tp的主要类

704 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第30天,点击查看活动详情

一、DtpRegistry类

分析源码从DtpRegistry类开始,这个类主要功能是 注册、获取、刷新 某个动态线程池,某个动态线程池是DtpExecutor类,先看DtpRegistry类属性

image.png

再看其它方法,获取当前存在的线程池名字:

=== 拿到所有线程池的名字 ===
    //获取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理解为加强版的儿子。

image.png

相比比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

该类主要是进行实体参数转化封装。

image.png

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即可。

image.png

八、RejectedInvocationHandler拒绝类

在任务负载过重达到拒绝策略的阈值,会执行指定的拒绝策略,在拒绝策略执行之前,需要发送报警信息,实现原理就是动态代理,在实际拒绝策略方法之前加上发送报警的增强方法beforeReject(executor);完成具体的逻辑RejectAware接口内默认方法,然后就是 RejectedInvocationHandler implements InvocationHandler, RejectedAware 重写 invoke方法。