当前位置: 首页 > news >正文

dubbo源码之优雅关闭

一、序言

        在分布式系统中,服务的启动和关闭是一个关键环节。特别是在生产环境中,如何优雅地关闭服务,确保正在处理的请求能够完成,避免数据丢失和用户体验的恶化,是每个开发者都需要关注的问题。

        Dubbo作为阿里巴巴开源的高性能RPC框架,在优雅关闭方面做了精心的设计。它不仅要处理Provider端正在执行的请求,还要处理Consumer端正在发送的请求,同时还要考虑注册中心的注销、线程池的关闭等复杂场景。

二、应用场景

为什么需要优雅关闭?
在实际生产环境中,服务重启、升级、扩缩容都是常见操作。如果简单粗暴地kill进程,会带来以下问题:

// 问题场景1:Provider端正在处理请求
public class OrderService {public void createOrder(OrderRequest request) {// 1. 参数校验validateRequest(request);// 2. 库存扣减 (如果这时进程被kill...)inventoryService.deduct(request.getProductId(), request.getQuantity());// 3. 创建订单 (这步就无法执行,导致库存扣了但订单没创建)orderRepository.save(buildOrder(request));// 4. 发送消息messageProducer.send(new OrderCreatedEvent(order));}
}
// 问题场景2:Consumer端正在等待响应
public class OrderController {@Autowiredprivate OrderService orderService;public ResponseEntity<String> createOrder(OrderRequest request) {try {// 如果Provider此时被强制关闭,会抛出异常orderService.createOrder(request);return ResponseEntity.ok("订单创建成功");} catch (Exception e) {// 用户会收到500错误return ResponseEntity.status(500).body("系统繁忙,请稍后重试");}}
}

三、源码解析

调用链路总览

// 场景1:应用正常关闭
kill -15 <pid>↓
JVM接收SIGTERM信号↓
自动执行shutdown hook↓
DubboShutdownHook.run()↓
最终调用gracefulShutdown()// 场景2:Spring Boot应用停止
@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);// 应用停止时Spring会触发ApplicationContext.close()// 进而触发Dubbo的关闭流程}
}// 场景3:容器环境关闭
# Docker容器停止
docker stop <container_id>↓
发送SIGTERM信号给主进程↓
触发gracefulShutdown()
JVM关闭信号↓
DubboShutdownHook.run()↓
DubboBootstrap.destroy()↓
ExtensionLoader.destroyAll()↓
Protocol实现类.destroy()↓
ExecutorRepository.destroyAll()↓
ExecutorUtil.gracefulShutdown()  ← 目标方法

3.1 第一层:JVM Shutdown Hook触发

// DubboShutdownHook.java
public class DubboShutdownHook extends Thread {@Overridepublic void run() {if (logger.isInfoEnabled()) {logger.info("Run shutdown hook now.");}destroyAll(); // 开始销毁所有资源}public void destroyAll() {DubboBootstrap.getInstance().destroy();}
}

3.2 第二层:DubboBootstrap销毁

// DubboBootstrap.java
public class DubboBootstrap {public void destroy() {// 销毁所有扩展加载器ExtensionLoader.destroyAll();// 销毁其他组件...destroyRegistries();destroyProtocols();}
}

3.3 第三层:ExtensionLoader销毁

// ExtensionLoader.java
public class ExtensionLoader<T> {public static void destroyAll() {// 销毁所有已加载的扩展实例for (ExtensionLoader<?> loader : EXTENSION_LOADERS.values()) {loader.destroy();}// 清理缓存EXTENSION_LOADERS.clear();}public void destroy() {// 销毁所有扩展实例for (Map.Entry<String, Holder<Object>> entry : cachedInstances.entrySet()) {Object instance = entry.getValue().get();if (instance != null) {try {// 如果实例实现了destroy方法,则调用destroyInstance(instance);} catch (Exception e) {logger.error("Error destroying extension " + instance, e);}}}}
}

3.4 第四层:Protocol实现类销毁

// DubboProtocol.java
public class DubboProtocol extends AbstractProtocol {@Overridepublic void destroy() {// 1. 关闭所有服务端for (String key : new ArrayList<>(serverMap.keySet())) {ProtocolServer protocolServer = serverMap.remove(key);if (protocolServer != null) {try {protocolServer.close();} catch (Throwable t) {logger.warn(t.getMessage(), t);}}}// 2. 关闭所有客户端for (String key : new ArrayList<>(referenceClientMap.keySet())) {ExchangeClient client = referenceClientMap.remove(key);if (client != null) {try {client.close();} catch (Throwable t) {logger.warn(t.getMessage(), t);}}}// 3. 销毁线程池 ← 关键调用点super.destroy();}
}// AbstractProtocol.java
public abstract class AbstractProtocol implements Protocol {@Overridepublic void destroy() {// 销毁所有线程池ExecutorRepository.getInstance().destroyAll();}
}

3.5 第五层:ExecutorRepository销毁

// ExecutorRepository.java
public class ExecutorRepository {private final Map<String, ExecutorService> data = new ConcurrentHashMap<>();public void destroyAll() {if (logger.isInfoEnabled()) {logger.info("Destroying all executors...");}// 遍历所有线程池进行销毁for (Map.Entry<String, ExecutorService> entry : data.entrySet()) {ExecutorService executor = entry.getValue();if (executor != null && !executor.isShutdown()) {try {// ← 调用目标方法ExecutorUtil.gracefulShutdown(executor, DEFAULT_SHUTDOWN_TIMEOUT);} catch (Exception e) {logger.warn("Error when destroy executor " + entry.getKey(), e);}}}// 清理缓存data.clear();}
}

3.6 第六层:线程池销毁

public static void gracefulShutdown(Executor executor, int timeout) {if (!(executor instanceof ExecutorService) || isTerminated(executor)) {return;}final ExecutorService es = (ExecutorService) executor;try {// Disable new tasks from being submittedes.shutdown();} catch (SecurityException ex2) {return;} catch (NullPointerException ex2) {return;}try {// Wait a while for existing tasks to terminateif (!es.awaitTermination(timeout, TimeUnit.MILLISECONDS)) {es.shutdownNow();}} catch (InterruptedException ex) {es.shutdownNow();Thread.currentThread().interrupt();}if (!isTerminated(es)) {newThreadToCloseExecutor(es);}}

这段代码其实就是经常说到的:等待任务完成之后再销毁线程池,实现任务处理的优雅策略。

四、总结

        本文介绍了经常用到的优雅关闭功能,那么有同学可能还有疑惑?那我不关闭、不优雅处理最直接的影响是啥?一句话概括就是不关闭资源可能不回收,不优雅业务可能受到影响。所以我们在实现一些功能的时候一定要做到流程闭环,在用的时候也要考虑到不用情况下的处理策略。

        欢迎关注、一起交流、一起进步。

http://www.xdnf.cn/news/1363915.html

相关文章:

  • 基于PyTorch深度学习遥感影像地物分类与目标检测、分割及遥感影像问题深度学习优化
  • 使用Docker配置Redis Stack集群的步骤
  • Redis常规指令及跳表
  • 电子之路(一)酒店门锁主板-主板接线图和原理-东方仙盟
  • 8.25学习日志
  • Portswigger靶场之Blind SQL injection with conditional errorsPRACTITIONERLAB
  • 36 NoSQL 注入
  • 大模型微调 Prompt Tuning与P-Tuning 的区别?
  • Java多态大冒险:当动物们开始“造反”
  • leetcode-hot-100 (二分查找)
  • 实用电脑小工具分享,守护电脑隐私与提升效率21/64
  • LengthFieldBasedFrameDecoder 详细用法
  • excel 破解工作表密码
  • 无锁队列的设计与实现
  • 记一次 element-plus el-table-v2 表格滚动卡顿问题优化
  • 【学习记录】CSS: clamp、@scope
  • 一键编译安装zabbix(centos)
  • Go编写的轻量文件监控器. 可以监控终端上指定文件夹内的变化, 阻止删除,修改,新增操作. 可以用于AWD比赛或者终端应急响应
  • go-redis库使用总结
  • 跨语言统一语义真理及其对NLP深层分析影响
  • 人体工学优化:握力环直径 / 重量设计与便携性、握持舒适度的协同分析
  • Spring Security(第五篇):从单体到前后端分离 —— JSON 响应与处理器实战
  • 0826xd
  • QtExcel/QXlsx
  • 力扣82:删除排序链表中的重复元素Ⅱ
  • 《Password Guessing Using Large Language Models》——论文阅读
  • 离线可用的网络急救方案
  • JavaScript Intl.RelativeTimeFormat:自动生成 “3 分钟前” 的国际化工具
  • [React]Antd Select组件输入搜索时调用接口
  • 基于RFM模型的客户群体大数据分析及用户聚类系统的设计与实现