Skip to main content

25小节:使用观察者模式重构配置动态刷新

作者:程序员马丁

在线博客:https://nageoffer.com

note

热门项目实战社群,收获国内众多知名公司面试青睐,近千名同学面试成功!助力你在校招或社招上拿个offer。

使用观察者模式重构配置动态刷新,元数据信息:

©版权所有 - 拿个offer-开源&项目实战星球专属学习项目,依据《中华人民共和国著作权法实施条例》《知识星球产权保护》,严禁未经本项目原作者明确书面授权擅自分享至 GitHub、Gitee 等任何开放平台。违者将面临法律追究。


内容摘要:本文深入剖析了 oneThread 动态线程池框架中跨模块通信的设计挑战,通过对配置中心 Starter 与 Web Starter 解耦需求的分析,引出观察者模式在框架架构中的核心价值。文章从实际业务场景出发,详细解析了观察者模式的设计理念、实现机制以及在动态线程池配置刷新中的具体应用。

课程目录如下所示:

  • 前言
  • 跨模块通信的设计挑战
  • 为什么选择观察者模式?
  • 什么是观察者模式?
  • oneThread 中的观察者模式实现
  • 扩展性分析
  • Guava vs Spring 观察者模式
  • 文末总结

前言

在构建 oneThread 动态线程池框架的过程中,我们面临着一个经典的架构设计问题:如何在保持模块独立性的前提下,实现跨模块的高效通信?

特别是在配置中心 Starter 需要通知 Web Starter 进行线程池参数更新时,传统的直接依赖方式会带来模块耦合、包体积膨胀等问题。为了解决这一挑战,我们引入了观察者模式,构建了一套松耦合、高扩展的事件驱动架构。

本文将带你深入了解这一设计决策的来龙去脉,以及观察者模式在 oneThread 框架中的精妙应用。

跨模块通信的设计挑战

1. 模块架构概览

oneThread 框架采用分层模块化设计,主要包含以下核心模块:

.
└── starter # 动态线程池配置中心组件包,实现线程池结合Spring框架和配置中心动态刷新
├── adapter # 动态线程池适配层,比如对接 Web 容器 Tomcat 线程池等
│   └── web-spring-boot-starter # Web 容器线程池组件库
├── apollo-spring-boot-starter # Apollo 配置中心动态监控线程池组件库
├── common-spring-boot-starter # 配置中心公共监听等逻辑抽象组件库
├── dashboard-dev-spring-boot-starter # 控制台 API 组件库
└── nacos-cloud-spring-boot-starter # Nacos 配置中心动态监控线程池组件库

和本文章相关的在 starter 模块下,子模块职责如下:

  • common-spring-boot-starter:提供配置解析、事件定义等公共能力。
  • nacos/apollo-spring-boot-starter:监听配置中心变更,触发刷新逻辑。
  • web-spring-boot-starter:管理 Web 容器线程池,响应配置变更。

如果采用直接依赖的方式解决跨模块通信,会面临以下问题:

  • 配置中心相关的 Starter 依赖 web-starter,如果用户仅需要动态线程池依赖,无法按需依赖。
  • 扩展性差,每增加一种线程池适配器(如 Dubbo、Hystrix 等),都需要修改配置中心 Starter 代码,增加新的依赖关系等。

代码耦合如下所示:

// 配置中心 Starter 需要了解所有可能的线程池类型
public class AbstractDynamicThreadPoolRefresher {

@Autowired(required = false)
private WebThreadPoolService webService;

@Autowired(required = false)
private RocketMQThreadPoolService rocketMQService;

@Autowired(required = false)
private RabbitMQThreadPoolService rabbitMQService;

public void refreshConfig(String config) {
if (webService != null) {
webService.update(config);
}
if (rocketMQService != null) {
rocketMQService.update(config);
}
// ... 更多if判断
}
}

2. 理想的解决方案

我们需要一种机制,能够完成以下需求:

  • 解耦模块依赖:配置中心 Starter 无需直接依赖具体的线程池管理模块。
  • 支持动态扩展:新增线程池适配器时,无需修改现有代码。
  • 保持高内聚:每个模块专注自己的核心职责。
  • 简化测试:模块间松耦合,便于单元测试和集成测试。

为什么选择观察者模式?

1. 业务场景分析

在 oneThread 框架中,配置变更的处理流程如下:

配置中心变更 → 配置解析 → 参数对比 → 线程池更新 → 变更通知

这个流程中存在典型的"一对多"通知场景:

  • 一个事件源:配置中心的配置变更。
  • 多个观察者:动态线程池管理器、Web 线程池管理器、RocketMQ 线程池管理器、自定义线程池管理器等。甚至加点想象力,是不是连接池动态变更也能做。

2. 观察者模式的优势

2.1 松耦合设计

2.2 动态扩展能力

新增线程池适配器时,只需:

  1. 开发监听者,实现 ApplicationListener<ThreadPoolConfigUpdateEvent> 接口。
  2. 将监听者注册为 Spring Bean,无需修改任何现有代码。

这种方案设计下,职责分离足够清晰:

  • 配置中心模块:专注配置监听和解析。
  • 线程池管理模块:不同组件专注自己职责内的线程池参数更新。
  • 事件机制:负责消息传递和路由。

什么是观察者模式?

1. 观察者模式定义

观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。

2. 核心角色

  • Subject(主题/被观察者):维护观察者列表,提供注册、移除观察者的方法。
  • Observer(观察者):定义更新接口,当收到主题通知时执行相应操作。
  • ConcreteSubject(具体主题):实现主题接口,状态改变时通知所有观察者。
  • ConcreteObserver(具体观察者):实现观察者接口,定义具体的更新逻辑。

3. 经典实现示例

以新闻订阅为例,展示观察者模式的基本实现:

// 观察者接口
public interface NewsObserver {
void update(String news);
}

// 主题接口
public interface NewsSubject {
void addObserver(NewsObserver observer);
void removeObserver(NewsObserver observer);
void notifyObservers(String news);
}

// 具体主题:新闻发布者
public class NewsPublisher implements NewsSubject {
private List<NewsObserver> observers = new ArrayList<>();

@Override
public void addObserver(NewsObserver observer) {
observers.add(observer);
}

@Override
public void removeObserver(NewsObserver observer) {
observers.remove(observer);
}

@Override
public void notifyObservers(String news) {
for (NewsObserver observer : observers) {
observer.update(news);
}
}

public void publishNews(String news) {
System.out.println("发布新闻: " + news);
notifyObservers(news);
}
}

// 具体观察者:邮件订阅者
public class EmailSubscriber implements NewsObserver {
private String email;

public EmailSubscriber(String email) {
this.email = email;
}

@Override
public void update(String news) {
System.out.println("邮件通知 " + email + ": " + news);
}
}

// 具体观察者:短信订阅者
public class SMSSubscriber implements NewsObserver {
private String phone;

public SMSSubscriber(String phone) {
this.phone = phone;
}

@Override
public void update(String news) {
System.out.println("短信通知 " + phone + ": " + news);
}
}

观察者模式时序图如下所示:

oneThread 中的观察者模式实现

1. Spring 事件机制

oneThread 框架基于 Spring 的事件发布机制实现观察者模式,这种方式具有以下优势:

  • 框架集成:与 Spring 容器深度集成,无需手动管理观察者列表。
  • 异步支持:支持同步和异步事件处理。
  • 类型安全:基于泛型的强类型事件定义。

解锁付费内容,👉 戳