dubbo源码之优雅关闭

一、序言

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

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

二、应用场景

为什么需要优雅关闭?

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

java 复制代码
// 问题场景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));
    }
}
java 复制代码
// 问题场景2:Consumer端正在等待响应
public class OrderController {
    @Autowired
    private 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("系统繁忙,请稍后重试");
        }
    }
}

三、源码解析

调用链路总览

XML 复制代码
// 场景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()
XML 复制代码
JVM关闭信号
    ↓
DubboShutdownHook.run()
    ↓
DubboBootstrap.destroy()
    ↓
ExtensionLoader.destroyAll()
    ↓
Protocol实现类.destroy()
    ↓
ExecutorRepository.destroyAll()
    ↓
ExecutorUtil.gracefulShutdown()  ← 目标方法

3.1 第一层:JVM Shutdown Hook触发

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

3.2 第二层:DubboBootstrap销毁

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

3.3 第三层:ExtensionLoader销毁

java 复制代码
// 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实现类销毁

java 复制代码
// DubboProtocol.java
public class DubboProtocol extends AbstractProtocol {
    
    @Override
    public 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 {
    
    @Override
    public void destroy() {
        // 销毁所有线程池
        ExecutorRepository.getInstance().destroyAll();
    }
}

3.5 第五层:ExecutorRepository销毁

java 复制代码
// 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 第六层:线程池销毁

java 复制代码
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 submitted
            es.shutdown();
        } catch (SecurityException ex2) {
            return;
        } catch (NullPointerException ex2) {
            return;
        }
        try {
            // Wait a while for existing tasks to terminate
            if (!es.awaitTermination(timeout, TimeUnit.MILLISECONDS)) {
                es.shutdownNow();
            }
        } catch (InterruptedException ex) {
            es.shutdownNow();
            Thread.currentThread().interrupt();
        }
        if (!isTerminated(es)) {
            newThreadToCloseExecutor(es);
        }
    }

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

四、总结

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

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