最近一段时间完成了一个简单的动态线程池的项目 有兴趣的读者可以去这两篇文章查看 juejin.cn/post/759586…
在开发过程中,我主要参考了 Hippo4j 的设计思路。我发现,很多看似复杂的动态配置功能,其实都是通过经典的设计模式解耦实现的。今天这篇通过复盘,我想把其中用到的核心设计模式单独拎出来,和大家聊聊它们是如何让线程池‘动’起来的
一.Builder设计模式
建造者设计模式绝对是我们日常开发中最常用的设计模式 我认为它和单例模式不相上下 我们都知道设计模式的最终目标都是设计出便于扩展的代码 解决构建灵活性与可维护性问题 在谈起 Builder设计模式 之前 先来讲一下没有构建者模式会出现什么问题
假设我们现在有一个动态线程池基类 它继承自ThreadPoolExecutor 扩展了一些简单的功能 比如说 线程Id
随着线程池功能逐步增强,我们需要引入更多自定义参数,例如:动态线程池标识、项目关闭时等待任务完成的最大时长等 但是我们发现 我们每次引入一个新的参数 就需要修改大量的代码 创建大量的构造器 这肯定是不利于扩展的
这时候就需要Builder设计模式了 在日常开发中 我们如果想使用Builder模式创建一个对象 只需要使用Lombok的@Builder注解就可以了
public DynamicThreadPool(String threadPoolId, int coreSize, int maxSize,
long keepAlive, TimeUnit unit, boolean waitForTasks,
long awaitSeconds, RejectedExecutionHandler handler) {
// ...赋值逻辑
}
// 调用时是一场噩梦:
new DynamicThreadPool("user-pool", 10, 20, 60, TimeUnit.SECONDS, true, 30, new AbortPolicy());
这是构建者模式
DynamicThreadPool pool = DynamicThreadPool.builder()
.threadPoolId("user-pool")
.corePoolSize(10)
.maxPoolSize(20)
.waitForTasksToCompleteOnShutdown(true) // 新增参数随加随用
.awaitTerminationSeconds(30)
.build();
但动态线程池需要对其中的一些核心参数进行校验 比如说线程池容量是否匹配 拒绝策略是否合法 等待时间是否超出我们的阈值 所以我们需要手写一个自己的Builder模式 来实现核心参数的校验 这里我提取了Hippo4j 的源码 建议想要学习这方面的读者可以去github.com/opengoofy/h… 下载源码自行观看:
private static AbstractBuildThreadPoolTemplate.ThreadPoolInitParam buildInitParam(ThreadPoolBuilder builder) {
AbstractBuildThreadPoolTemplate.ThreadPoolInitParam initParam;
if (builder.threadFactory == null) {
Assert.notEmpty(builder.threadNamePrefix, "The thread name prefix cannot be empty or an empty string.");
initParam = new AbstractBuildThreadPoolTemplate.ThreadPoolInitParam(builder.threadNamePrefix, builder.isDaemon);
} else {
initParam = new AbstractBuildThreadPoolTemplate.ThreadPoolInitParam(builder.threadFactory);
}
initParam.setCorePoolNum(builder.corePoolSize)
.setMaximumPoolSize(builder.maximumPoolSize)
.setKeepAliveTime(builder.keepAliveTime)
.setCapacity(builder.capacity)
.setExecuteTimeOut(builder.executeTimeOut)
.setRejectedExecutionHandler(builder.rejectedExecutionHandler)
.setTimeUnit(builder.timeUnit)
.setAllowCoreThreadTimeOut(builder.allowCoreThreadTimeOut)
.setTaskDecorator(builder.taskDecorator);
if (builder.isDynamicPool) {
String threadPoolId = Optional.ofNullable(builder.threadPoolId).orElse(builder.threadNamePrefix);
initParam.setThreadPoolId(threadPoolId);
initParam.setWaitForTasksToCompleteOnShutdown(builder.waitForTasksToCompleteOnShutdown);
initParam.setAwaitTerminationMillis(builder.awaitTerminationMillis);
}
if (!builder.isFastPool) {
if (builder.workQueue == null) {
if (builder.blockingQueueType == null) {
builder.blockingQueueType = BlockingQueueTypeEnum.LINKED_BLOCKING_QUEUE;
}
builder.workQueue = BlockingQueueTypeEnum.createBlockingQueue(builder.blockingQueueType.getType(), builder.capacity);
}
initParam.setWorkQueue(builder.workQueue);
}
return initParam;
}
@Override
public ThreadPoolExecutor build() {
return isDynamicPool ? buildDynamicPool(this) : buildPool(this);
}
可以看到 其builder对核心参数进行了校验 这就是我们自己手写builder设计模式的好处
二.简单工厂模式
简而言之,就是把“创建对象”的过程封装到一个单独的类(工厂)中,让调用者不需要关心具体的创建逻辑。
在没有工厂模式时,如果我们需要一个对象,通常直接使用 new 关键字。 而在简单工厂模式中,我们将对象的创建权利交给一个“工厂类”。客户端只需要告诉工厂“我想要什么(参数)”,工厂就会返回对应的对象。
可以看一下实战代码:
CALLER_RUNS_POLICY(1, "CallerRunsPolicy", new ThreadPoolExecutor.CallerRunsPolicy()),
ABORT_POLICY(2, "AbortPolicy", new ThreadPoolExecutor.AbortPolicy()),
DISCARD_POLICY(3, "DiscardPolicy", new ThreadPoolExecutor.DiscardPolicy()),
DISCARD_OLDEST_POLICY(4, "DiscardOldestPolicy", new ThreadPoolExecutor.DiscardOldestPolicy()),
RUNS_OLDEST_TASK_POLICY(5, "RunsOldestTaskPolicy", new RunsOldestTaskPolicy()),
SYNC_PUT_QUEUE_POLICY(6, "SyncPutQueuePolicy", new SyncPutQueuePolicy());
@Getter
private Integer type;
@Getter
private String name;
private RejectedExecutionHandler rejectedHandler;
RejectedPolicyTypeEnum(Integer type, String name, RejectedExecutionHandler rejectedHandler) {
this.type = type;
this.name = name;
this.rejectedHandler = rejectedHandler;
}
static {
ServiceLoaderRegistry.register(CustomRejectedExecutionHandler.class);
}
public static RejectedExecutionHandler createPolicy(String name) {
RejectedPolicyTypeEnum rejectedTypeEnum = Stream.of(RejectedPolicyTypeEnum.values())
.filter(each -> Objects.equals(each.name, name))
.findFirst()
.orElse(null);
if (rejectedTypeEnum != null) {
return rejectedTypeEnum.rejectedHandler;
}
Collection<CustomRejectedExecutionHandler> customRejectedExecutionHandlers = ServiceLoaderRegistry
.getSingletonServiceInstances(CustomRejectedExecutionHandler.class);
Optional<RejectedExecutionHandler> customRejected = customRejectedExecutionHandlers.stream()
.filter(each -> Objects.equals(name, each.getName()))
.map(each -> each.generateRejected())
.findFirst();
return customRejected.orElse(ABORT_POLICY.rejectedHandler);
}
public static RejectedExecutionHandler createPolicy(int type) {
Optional<RejectedExecutionHandler> rejectedTypeEnum = Stream.of(RejectedPolicyTypeEnum.values())
.filter(each -> Objects.equals(type, each.type))
.map(each -> each.rejectedHandler)
.findFirst();
RejectedExecutionHandler resultRejected = rejectedTypeEnum.orElseGet(() -> {
Collection<CustomRejectedExecutionHandler> customRejectedExecutionHandlers = ServiceLoaderRegistry
.getSingletonServiceInstances(CustomRejectedExecutionHandler.class);
Optional<RejectedExecutionHandler> customRejected = customRejectedExecutionHandlers.stream()
.filter(each -> Objects.equals(type, each.getType()))
.map(each -> each.generateRejected())
.findFirst();
return customRejected.orElse(ABORT_POLICY.rejectedHandler);
});
return resultRejected;
}
public static String getRejectedNameByType(int type) {
return createPolicy(type).getClass().getSimpleName();
}
public static RejectedPolicyTypeEnum getRejectedPolicyTypeEnumByName(String name) {
Optional<RejectedPolicyTypeEnum> rejectedTypeEnum = Stream.of(RejectedPolicyTypeEnum.values())
.filter(each -> each.name.equals(name))
.findFirst();
return rejectedTypeEnum.orElse(ABORT_POLICY);
}
当你向这个工厂类传入一个拒绝策略名称或者类型的时候 它会在内部校验是否合法 然后返回一个拒绝策略类 假设我们不使用这个工厂 就得在业务层写逻辑校验 而且每当你添加一个拒绝策略的时候 大量涉及到逻辑校验的地方都需要修改
同样的还有阻塞队列的设计 前文已经提到过 我们需要自己设计阻塞队列所以工厂模式也是必不可少的
/**
* {@link java.util.concurrent.ArrayBlockingQueue}
*/
ARRAY_BLOCKING_QUEUE(1, "ArrayBlockingQueue") {
@Override
<T> BlockingQueue<T> of(Integer capacity) {
return new ArrayBlockingQueue<>(capacity);
}
@Override
<T> BlockingQueue<T> of() {
return new ArrayBlockingQueue<>(DEFAULT_CAPACITY);
}
},
/**
* {@link java.util.concurrent.LinkedBlockingQueue}
*/
LINKED_BLOCKING_QUEUE(2, "LinkedBlockingQueue") {
@Override
<T> BlockingQueue<T> of(Integer capacity) {
return new LinkedBlockingQueue<>(capacity);
}
@Override
<T> BlockingQueue<T> of() {
return new LinkedBlockingQueue<>();
}
},
/**
* {@link java.util.concurrent.LinkedBlockingDeque}
*/
LINKED_BLOCKING_DEQUE(3, "LinkedBlockingDeque") {
@Override
<T> BlockingQueue<T> of(Integer capacity) {
return new LinkedBlockingDeque<>(capacity);
}
@Override
<T> BlockingQueue<T> of() {
return new LinkedBlockingDeque<>();
}
},
/**
* {@link java.util.concurrent.SynchronousQueue}
*/
SYNCHRONOUS_QUEUE(4, "SynchronousQueue") {
@Override
<T> BlockingQueue<T> of(Integer capacity) {
return new SynchronousQueue<>();
}
@Override
<T> BlockingQueue<T> of() {
return new SynchronousQueue<>();
}
},
/**
* {@link java.util.concurrent.LinkedTransferQueue}
*/
LINKED_TRANSFER_QUEUE(5, "LinkedTransferQueue") {
@Override
<T> BlockingQueue<T> of(Integer capacity) {
return new LinkedTransferQueue<>();
}
@Override
<T> BlockingQueue<T> of() {
return new LinkedTransferQueue<>();
}
},
/**
* {@link java.util.concurrent.PriorityBlockingQueue}
*/
PRIORITY_BLOCKING_QUEUE(6, "PriorityBlockingQueue") {
@Override
<T> BlockingQueue<T> of(Integer capacity) {
return new PriorityBlockingQueue<>(capacity);
}
@Override
<T> BlockingQueue<T> of() {
return new PriorityBlockingQueue<>();
}
},
/**
* {@link ResizableCapacityLinkedBlockingQueue}
*/
RESIZABLE_LINKED_BLOCKING_QUEUE(9, "ResizableCapacityLinkedBlockingQueue") {
@Override
<T> BlockingQueue<T> of(Integer capacity) {
return new ResizableCapacityLinkedBlockingQueue<>(capacity);
}
@Override
<T> BlockingQueue<T> of() {
return new ResizableCapacityLinkedBlockingQueue<>();
}
};
@Getter
private final Integer type;
@Getter
private final String name;
/**
* Create the specified implement of BlockingQueue with init capacity.
* Abstract method, depends on sub override
*
* @param capacity the capacity of the queue
* @param <T> the class of the objects in the BlockingQueue
* @return a BlockingQueue view of the specified T
*/
abstract <T> BlockingQueue<T> of(Integer capacity);
/**
* Create the specified implement of BlockingQueue,has no capacity limit.
* Abstract method, depends on sub override
*
* @param <T> the class of the objects in the BlockingQueue
* @return a BlockingQueue view of the specified T
*/
abstract <T> BlockingQueue<T> of();
BlockingQueueTypeEnum(int type, String name) {
this.type = type;
this.name = name;
}
private static final Map<Integer, BlockingQueueTypeEnum> TYPE_TO_ENUM_MAP;
private static final Map<String, BlockingQueueTypeEnum> NAME_TO_ENUM_MAP;
static {
final BlockingQueueTypeEnum[] values = BlockingQueueTypeEnum.values();
TYPE_TO_ENUM_MAP = new HashMap<>(values.length);
NAME_TO_ENUM_MAP = new HashMap<>(values.length);
for (BlockingQueueTypeEnum value : values) {
TYPE_TO_ENUM_MAP.put(value.type, value);
NAME_TO_ENUM_MAP.put(value.name, value);
}
}
/**
* Creates a BlockingQueue with the given {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name}
* and capacity.
*
* @param blockingQueueName {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name}
* @param capacity the capacity of the BlockingQueue
* @param <T> the class of the objects in the BlockingQueue
* @return a BlockingQueue view of the specified T
*/
private static <T> BlockingQueue<T> of(String blockingQueueName, Integer capacity) {
final BlockingQueueTypeEnum typeEnum = NAME_TO_ENUM_MAP.get(blockingQueueName);
if (typeEnum == null) {
return null;
}
return Objects.isNull(capacity) ? typeEnum.of() : typeEnum.of(capacity);
}
/**
* Creates a BlockingQueue with the given {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type}
* and capacity.
*
* @param type {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type}
* @param capacity the capacity of the BlockingQueue
* @param <T> the class of the objects in the BlockingQueue
* @return a BlockingQueue view of the specified T
*/
private static <T> BlockingQueue<T> of(int type, Integer capacity) {
final BlockingQueueTypeEnum typeEnum = TYPE_TO_ENUM_MAP.get(type);
if (typeEnum == null) {
return null;
}
return Objects.isNull(capacity) ? typeEnum.of() : typeEnum.of(capacity);
}
private static final int DEFAULT_CAPACITY = 1024;
static {
ServiceLoaderRegistry.register(CustomBlockingQueue.class);
}
private static <T> BlockingQueue<T> customOrDefaultQueue(Integer capacity, Predicate<CustomBlockingQueue> predicate) {
Collection<CustomBlockingQueue> customBlockingQueues = ServiceLoaderRegistry
.getSingletonServiceInstances(CustomBlockingQueue.class);
Integer resolvedCapacity = capacity;
if (resolvedCapacity == null || resolvedCapacity <= 0) {
resolvedCapacity = DEFAULT_CAPACITY;
}
Integer finalResolvedCapacity = resolvedCapacity;
return customBlockingQueues.stream()
.filter(predicate)
.map(each -> each.generateBlockingQueue(finalResolvedCapacity))
.findFirst()
.orElseGet(() -> new LinkedBlockingQueue<T>(finalResolvedCapacity));
}
/**
* Creates a BlockingQueue with the given {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name}
* and capacity. if can't find the blockingQueueName with {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name},
* create custom or default BlockingQueue {@link BlockingQueueTypeEnum#customOrDefaultQueue BlockingQueueTypeEnum.customOrDefaultQueue}.
*
* @param blockingQueueName {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name}
* @param capacity the capacity of the BlockingQueue
* @param <T> the class of the objects in the BlockingQueue
* @return a BlockingQueue view of the specified T
*/
public static <T> BlockingQueue<T> createBlockingQueue(String blockingQueueName, Integer capacity) {
final BlockingQueue<T> of = of(blockingQueueName, capacity);
if (of != null) {
return of;
}
return customOrDefaultQueue(capacity,
(customerQueue) -> Objects.equals(customerQueue.getName(), blockingQueueName));
}
/**
* Creates a BlockingQueue with the given {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type}
* and capacity. if can't find the blockingQueueName with {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type},
* create custom or default BlockingQueue {@link BlockingQueueTypeEnum#customOrDefaultQueue BlockingQueueTypeEnum.customOrDefaultQueue}.
*
* @param type {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type}
* @param capacity the capacity of the BlockingQueue
* @param <T> the class of the objects in the BlockingQueue
* @return a BlockingQueue view of the specified T
*/
public static <T> BlockingQueue<T> createBlockingQueue(int type, Integer capacity) {
final BlockingQueue<T> of = of(type, capacity);
if (of != null) {
return of;
}
return customOrDefaultQueue(capacity,
(customeQueue) -> Objects.equals(customeQueue.getType(), type));
}
/**
* Map {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type } to {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name }
* or "" if can't mapping.
*
* @param type {@link BlockingQueueTypeEnum#type BlockingQueueTypeEnum.type}
* @return {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name } or "".
*/
public static String getBlockingQueueNameByType(int type) {
return Optional.ofNullable(TYPE_TO_ENUM_MAP.get(type))
.map(value -> value.getName())
.orElse("");
}
/**
* find {@link BlockingQueueTypeEnum} by {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name }
* or {@link BlockingQueueTypeEnum#LINKED_BLOCKING_QUEUE} if can't mapping.
*
* @param name {@link BlockingQueueTypeEnum#name BlockingQueueTypeEnum.name }
* @return enum {@link BlockingQueueTypeEnum}
*/
public static BlockingQueueTypeEnum getBlockingQueueTypeEnumByName(String name) {
return Optional.ofNullable(NAME_TO_ENUM_MAP.get(name))
.orElse(LINKED_BLOCKING_QUEUE);
}
三.代理模式
线程池作为任务并发处理的核心组件,其稳定性直接影响系统整体吞吐与响应能力。而线程池中的拒绝策略 ,作为最后一道防线,往往代表了系统已出现短时瓶颈或配置不合理等问题
当出现拒绝策略的时候 我们想要 上报报警 或者说记录指标
但原生的这种拒绝策略不支持我们完成这一点 所以就需要代理模式
这个模式我在第一篇文章做过详细的说明 此处为了节省篇幅 不做过多赘述 juejin.cn/post/759586…
四.观察者模式
大多数动态线程池都会支持其他组件的线程池 比如Hippo4j 它支持了 如此多的组件
在动态线程池的演进过程中,多组件适配带来的代码耦合问题日益凸显。例如,为了支持 Tomcat 或 RabbitMQ 的线程池动态调整,我们往往需要在核心业务层硬编码大量的特定校验逻辑。
这种做法严重违背了**‘对扩展开放,对修改关闭’(Open/Closed Principle)**的设计原则。我们需要构建一种更优雅的适配机制,使得核心业务层无需感知具体组件的存在,而是通过抽象层面的统一调用,实现对各类异构线程池的无缝兼容。
我们需要一种机制,能够完成以下需求:
- 解耦模块依赖:配置中心 Starter 无需直接依赖具体的线程池管理模块。
- 支持动态扩展:新增线程池适配器时,无需修改现有代码。
- 保持高内聚:每个模块专注自己的核心职责。
- 简化测试:模块间松耦合,便于单元测试和集成测试。
这时我们就需要引入观察者模式 观察者模式是一对多的模型 一个事件源对应多个观察者
观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新
我们需要定义
- Subject(主题/被观察者) :维护观察者列表,提供注册、移除观察者的方法。
- Observer(观察者) :定义更新接口,当收到主题通知时执行相应操作。
- ConcreteSubject(具体主题) :实现主题接口,状态改变时通知所有观察者。
- ConcreteObserver(具体观察者) :实现观察者接口,定义具体的更新逻辑。
下面给出简单Demo帮你理解观察者模式
为了让你一秒看懂,我们用**“微信群”**来举例:
- 被观察者(Subject): 微信群(群主发消息)。
- 观察者(Observer): 群成员(手机收到弹窗)。
1. 两个核心接口(标准)
Java
// 1. 【接收者标准】观察者接口
// 所有的“群成员”都必须实现这个接口,才能收到消息
interface Observer {
void update(String message);
}
// 2. 【发送者标准】主题接口
// 定义“群”的基本功能:拉人进群、踢人、群发消息
interface Subject {
void attach(Observer observer); // 关注/进群
void detach(Observer observer); // 取消关注/退群
void notifyObservers(String message); // 通知所有
}
2. 具体实现类
Java
import java.util.ArrayList;
import java.util.List;
// 3. 【具体的发送者】微信群
class WeChatGroup implements Subject {
// 通讯录:存所有群成员
private List<Observer> members = new ArrayList<>();
@Override
public void attach(Observer observer) {
members.add(observer);
}
@Override
public void detach(Observer observer) {
members.remove(observer);
}
@Override
public void notifyObservers(String message) {
// 核心逻辑:遍历名单,挨个调用 update
for (Observer member : members) {
member.update(message);
}
}
}
// 4. 【具体的接收者】用户
class User implements Observer {
private String name;
public User(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " 收到消息: " + message);
}
}
3. 测试运行
Java
public class SimpleDemo {
public static void main(String[] args) {
// 1. 创建一个群(被观察者)
WeChatGroup group = new WeChatGroup();
// 2. 创建三个用户(观察者)
User userA = new User("张三");
User userB = new User("李四");
User userC = new User("王五");
// 3. 进群(注册)
group.attach(userA);
group.attach(userB);
group.attach(userC);
// 4. 群主发消息(触发通知)
System.out.println("====== 第一次群发 ======");
group.notifyObservers("今晚8点发红包!");
// 5. 王五退群(移除)
group.detach(userC);
// 6. 再次发消息
System.out.println("\n====== 第二次群发 ======");
group.notifyObservers("红包发完了!");
}
}
4. 运行结果
Plaintext
====== 第一次群发 ======
张三 收到消息: 今晚8点发红包!
李四 收到消息: 今晚8点发红包!
王五 收到消息: 今晚8点发红包!
====== 第二次群发 ======
张三 收到消息: 红包发完了!
李四 收到消息: 红包发完了!
总的来说 不管多复杂的框架(Spring Event、Vue 的双向绑定、Hippo4j 的动态刷新),底层逻辑永远只有这三步:
- 存起来: 用一个
List把想要听消息的人存着。 - 发生变化: 触发某个事件。
- 遍历调方法: 循环
List,调用每个人的.update()方法。
下面来讲观察者模式在动态线程池的使用 上面提到了如果我们不做任何设计 在业务层就需要做许多逻辑校验 不利于扩展 所以我们可以利用 Spring 事件机制来实现观察者模式
- 事件定义 (
ThreadPoolConfigUpdateEvent) :定义了“发生了什么”(配置更新了)以及“携带的数据”(最新的配置属性)。 - 事件发布者 (
AbstractDynamicThreadPoolRefresher) :作为 Subject(被观察者),负责感知外部配置变化,解析数据,然后广播事件。它不需要知道谁在监听,只管喊一声。 - 事件监听者 (
Listener) :作为 Observer(观察者),实现了ApplicationListener。不同的监听器关注不同的业务(一个管普通线程池,一个管 Web 容器线程池),它们同时监听同一个事件,互不干扰。
Java
/* * ========================================================================
* 1. 【事件定义】(The Message)
* 继承 Spring 的 ApplicationEvent,作为观察者模式中传递数据的载体。
* ========================================================================
*/
public class ThreadPoolConfigUpdateEvent extends ApplicationEvent {
@Getter
@Setter
private BootstrapConfigProperties bootstrapConfigProperties;
/**
* @param source 事件源(谁发出的事件)
* @param bootstrapConfigProperties 携带的核心数据(最新的配置)
*/
public ThreadPoolConfigUpdateEvent(Object source, BootstrapConfigProperties bootstrapConfigProperties) {
super(source);
this.bootstrapConfigProperties = bootstrapConfigProperties;
}
}
/* * ========================================================================
* 2. 【事件发布者 / 被观察者】(Subject)
* 负责解析配置,并利用 Spring 上下文广播事件。
* 这里的核心优势是:它不需要依赖任何具体的 ThreadPoolService。
* ========================================================================
*/
@Slf4j
@RequiredArgsConstructor
public abstract class AbstractDynamicThreadPoolRefresher implements ApplicationRunner {
protected final BootstrapConfigProperties properties;
/**
* 配置刷新的入口方法
* 流程:Nacos/Apollo 推送字符串 -> 解析为对象 -> 广播事件
*/
@SneakyThrows
public void refreshThreadPoolProperties(String configInfo) {
// [Step 1] 解析配置:将简单的 String 解析为 Map
Map<Object, Object> configInfoMap = ConfigParserHandler.getInstance()
.parseConfig(configInfo, properties.getConfigFileType());
// [Step 2] 数据绑定:利用 Spring Binder 将 Map 绑定到强类型配置对象 properties 中
ConfigurationPropertySource sources = new MapConfigurationPropertySource(configInfoMap);
Binder binder = new Binder(sources);
BootstrapConfigProperties refresherProperties = binder
.bind(BootstrapConfigProperties.PREFIX, Bindable.ofInstance(properties))
.get();
// [Step 3] ★★★ 核心设计模式体现:发布事件 ★★★
// 这里完全解耦:refresher 不调用 service.update(),而是广播 "配置变了" 的消息。
// ApplicationContextHolder 是 Spring Context 的工具类
ApplicationContextHolder.publishEvent(
new ThreadPoolConfigUpdateEvent(this, refresherProperties)
);
}
}
/* * ========================================================================
* 3. 【具体观察者 A】(Observer - 普通线程池)
* 监听 ThreadPoolConfigUpdateEvent 事件,负责刷新业务中的动态线程池。
* ========================================================================
*/
@Slf4j
@RequiredArgsConstructor
public class DynamicThreadPoolRefreshListener implements ApplicationListener<ThreadPoolConfigUpdateEvent> {
private final NotifierDispatcher notifierDispatcher;
@Override
public void onApplicationEvent(ThreadPoolConfigUpdateEvent event) {
// 1. 从事件中取出最新的配置数据
BootstrapConfigProperties refresherProperties = event.getBootstrapConfigProperties();
// 2. 校验:如果没有相关配置,直接跳过
if (CollUtil.isEmpty(refresherProperties.getExecutors())) {
return;
}
// 3. 执行具体的业务逻辑:遍历并更新线程池参数
for (ThreadPoolExecutorProperties remoteProperties : refresherProperties.getExecutors()) {
// ... (省略具体更新代码:比如 setCorePoolSize, setMaximumPoolSize)
// checkAndRefresh(remoteProperties);
}
}
}
/* * ========================================================================
* 4. 【具体观察者 B】(Observer - Web容器线程池)
* 同样监听 ThreadPoolConfigUpdateEvent 事件,但只关注 Web 容器(如 Tomcat)的配置。
* * ★ 设计亮点:通过增加一个新的 Listener 类来扩展功能,完全符合【开闭原则】。
* 如果未来要支持 RocketMQ 线程池刷新,只需再写一个 RocketMQThreadPoolRefreshListener 即可,
* 无需修改 Refresher 和其他的 Listener。
* ========================================================================
*/
@RequiredArgsConstructor
public class WebThreadPoolRefreshListener implements ApplicationListener<ThreadPoolConfigUpdateEvent> {
private final WebThreadPoolService webThreadPoolService;
private final NotifierDispatcher notifierDispatcher;
@Override
public void onApplicationEvent(ThreadPoolConfigUpdateEvent event) {
// 1. 获取 Web 相关的配置子集
BootstrapConfigProperties.WebThreadPoolExecutorConfig webExecutorConfig = event.getBootstrapConfigProperties().getWeb();
if (Objects.isNull(webExecutorConfig)) {
return;
}
// 2. 获取当前运行时的 Web 线程池指标(用于对比是否发生变化)
WebThreadPoolBaseMetrics basicMetrics = webThreadPoolService.getBasicMetrics();
// 3. 对比:只有核心参数(核心数、最大数、空闲时间)变了才执行刷新
if (!Objects.equals(basicMetrics.getCorePoolSize(), webExecutorConfig.getCorePoolSize())
|| !Objects.equals(basicMetrics.getMaximumPoolSize(), webExecutorConfig.getMaximumPoolSize())
|| !Objects.equals(basicMetrics.getKeepAliveTime(), webExecutorConfig.getKeepAliveTime())) {
// [Action] 执行 Tomcat/Jetty 线程池的参数修改
webThreadPoolService.updateThreadPool(BeanUtil.toBean(webExecutorConfig, WebThreadPoolConfig.class));
// [Notify] 发送变更通知(钉钉/企业微信)
sendWebThreadPoolConfigChangeMessage(basicMetrics, webExecutorConfig);
}
}
// ... 辅助方法 sendWebThreadPoolConfigChangeMessage ...
}
假设我们需要再添加一个支持RocketMq线程池 只需要创建监听器 实现配置类 自定义事件即可 消除了耦合
结尾
回顾整个动态线程池的开发过程,从最初的一个简单 ThreadPoolExecutor 包装类,到如今支持多组件适配、动态热更新、监控告警的完整中间件,设计模式在其中扮演了“骨架”与“灵魂”的角色。
- Builder 模式 让我们从冗长的构造参数中解脱,赋予了代码优雅的构建能力;
- 简单工厂模式 屏蔽了复杂的对象创建逻辑,让策略的扩展变得从容;
- 代理模式 为我们在不侵入业务代码的前提下,装上了监控的“眼睛”;
- 观察者模式 则打通了配置中心与应用内部的任督二脉,实现了真正意义上的“动态”与“解耦”。
很多人常说“不要重复造轮子”,但我认为,为了学习而造轮子是程序员进阶的必经之路。当我们不再满足于仅仅“使用”API,而是开始思考“由于什么痛点,所以使用了什么模式,最终解决了什么问题”时,我们的架构思维才真正开始觉醒。
。希望这篇文章能给你带来一些关于源码阅读与架构设计的启发。如果你对代码细节感兴趣,欢迎点击开头的链接查看我的前两篇文章,也欢迎在评论区与我交流!