面试官:你知道Dubbo怎么优雅上下线的吗?你:优雅上下线是啥?
bigegpt 2025-06-24 11:56 7 浏览
最近无论是校招还是社招,都进行的如火如荼,我也承担了很多的面试工作,在一次面试过程中,和候选人聊了一些关于Dubbo的知识。
Dubbo是一个比较著名的RPC框架,很多人对于他的一些网络通信、通信协议、动态代理等等都有一定的了解,这位候选人也一样。
但是,我接下来问了他一个问题:你们在使用Dubbo的时候,应用如果重启,怎么保证一个请求不会被中断处理的呢?
他没怎么说的上来,我以为他不理解我的问题,我接着问他:我就是想问下Dubbo是如何做优雅上下线的你知道吗?
接着他问我:优雅上下线是啥??
好吧。
这篇文章,我来介绍一下这个知识点吧。
优雅上下线
关于"优雅上下线"这个词,我没找到官方的解释,我尝试解释一下这是什么。
首先,上线、下线大家一定都很清楚,比如我们一次应用发布过程中,就需要先将应用服务停掉,然后再把服务启动起来。这个过程就包含了一次下线和一次上线。
那么,"优雅"怎么理解呢?
先说什么情况我们认为不优雅:
1、服务停止时,没有关闭对应的监控,导致应用停止后发生大量报警。
2、应用停止时,没有通知外部调用方,很多请求还会过来,导致很多调用失败。
3、应用停止时,有线程正在执行中,执行了一半,JVM进程就被干掉了。
4、应用启动时,服务还没准备好,就开始对外提供服务,导致很多失败调用。
5、应用启动时,没有检查应用的健康状态,就开始对外提供服务,导致很多失败调用。
以上,都是我们认为的不优雅的情况,那么,反过来,优雅上下线就是一种避免上述情况发生的手段。
一个应用的优雅上下线涉及到的内容其实有很多,从底层的操作系统、容器层面,到编程语言、框架层面,再到应用架构层面,涉及到的知识很广泛。
其实,优雅上下线中,最重要的还是优雅下线。因为如果下线过程不优雅的话,就会发生很多调用失败了、服务找不到等问题。所以很多时候,大家也会提优雅停机这样的概念。
本文后面介绍的优雅上下线也重点关注优雅停机的过程。
操作系统&容器的优雅上下线
关于操作系统,我之前有一篇文章专门介绍过这个话题,可能大家没有注意到,那时候介绍的主题是为什么不能在线上机器中随便执行kill -9。
其实,这背后的思考就是优雅上下线。
我们知道,kill -9之所以不建议使用,是因为kill -9特别强硬,系统会发出SIGKILL信号,他要求接收到该信号的程序应该立即结束运行,不能被阻塞或者忽略。
这个过程显然是不优雅的,因为应用立刻停止的话,就没办法做收尾动作。而更优雅的方式是kill -15。
当使用kill -15时,系统会发送一个SIGTERM的信号给对应的程序。当程序接收到该信号后,具体要如何处理是自己可以决定的。
kill -15会通知到应用程序,这就是操作系统对于优雅上下线的最基本的支持。
以前,在操作系统之上就是应用程序了,但是,自从容器化技术推出之后,在操作系统和应用程序之间,多了一个容器层,而Docker、k8s等容器其实也是支持优雅上下线的。
如Docker中同样提供了两个命令, docker stop 和 docker kill
docker stop就像kill -15一样,他会向容器内的进程发送SIGTERM信号,在10S之后(可通过参数指定)再发送SIGKILL信号。
而docker kill就像kill -9,直接发送SIGKILL信号。
JVM的优雅上下线
在操作系统、容器等对优雅上下线有了基本的支持之后,在接收到docker stop、kill -15等命令后,会通知应用进程进行进程关闭。
而Java应用在运行时就是一个独立运行的进程,这个进程是如何关闭的呢?
Java程序的终止运行是基于JVM的关闭实现的,JVM关闭方式分为正常关闭、强制关闭和异常关闭3种。
这其中,正常关闭就是支持优雅上下线的。正常关闭过程中,JVM可以做一些清理动作,比如删除临时文件。
当然,开发者也是可以自定义做一些额外的事情的,比如通知应用框架优雅上下线操作。
而这种机制是通过JDK中提供的shutdown hook实现的。JDK提供了Java.Runtime.addShutdownHook(Thread hook)方法,可以注册一个JVM关闭的钩子。
例子如下:
package com.hollis;
public class ShutdownHookTest {
public static void main(String[] args) {
boolean flag = true;
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("hook execute...");
}));
while (flag) {
// app is runing
}
System.out.println("main thread execute end...");
}
}
执行命令:
jps
6520 ShutdownHookTest
6521 Jps
kill 6520
控制台输出内容:
hook execute...
Process finished with exit code 143 (interrupted by signal 15: SIGTERM)
可以看到,当我们使用kill(默认kill -15)关闭进程的时候,程序会先执行我注册的shutdownHook,然后再退出,并且会给出一个提示:interrupted by signal 15: SIGTERM
Spring的优雅上下线
有了JVM提供的shutdown hook之后,很多框架都可以通过这个机制来做优雅下线的支持。
比如Spring,他就会向JVM注册一个shutdown hook,在接收到关闭通知的时候,进行bean的销毁,容器的销毁处理等操作。
同时,作为一个成熟的框架,Spring也提供了事件机制,可以借助这个机制实现更多的优雅上下线功能。
ApplicationListener是Spring事件机制的一部分,与抽象类ApplicationEvent类配合来完成ApplicationContext的事件机制。
开发者可以实现ApplicationListener接口,监听到 Spring 容器的关闭事件(ContextClosedEvent),来做一些特殊的处理:
@Component
public class MyListener implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
// 做容器关闭之前的清理工作
}
}
Dubbo的优雅上下线
因为Spring中提供了ApplicationListener接口,帮助我们来监听容器关闭事件,那么,很多web容器、框架等就可以借助这个机制来做自己的优雅上下线操作。
如tomcat、dubbo等都是这么做的。
这里简答说一下Dubbo的,在Dubbo的官网中,有关于优雅停机的介绍:
应用在停机时,接收到关闭通知时,会先把自己标记为不接受(发起)新请求,然后再等待10s(默认是10秒)的时候,等执行中的线程执行完。
那么,之所以他能做这些事,是因为从操作系统、到JVM、到Spring等都对优雅停机做了很好的支持。
关于Dubbo各个版本中具体是如何借助JVM的shutdown hook机制、或者说Spring的事件机制的优雅停机,我的一位同事的一篇文章介绍的很清晰,大家可以看下:
https://www.cnkirito.moe/dubbo-gracefully-shutdown/
在从Dubbo 2.5 到 Dubbo 2.7介绍了历史版本中,Dubbo为了解决优雅上下线问题所遇到的问题和方案。
目前,Dubbo中实现方式如下,同样是用到了Spring的事件机制:
public class SpringExtensionFactory implements ExtensionFactory {
public static void addApplicationContext(ApplicationContext context) {
CONTEXTS.add(context);
if (context instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) context).registerShutdownHook();
DubboShutdownHook.getDubboShutdownHook().unregister();
}
BeanFactoryUtils.addApplicationListener(context, SHUTDOWN_HOOK_LISTENER);
}
}
总结
本文从操作系统开始,分别介绍了Linux、Docker、JVM、Spring、Dubbo等对优雅停机的支持。
可以看到,一个简单的优雅停机功能,上下游需要这么多底层基础设施和上层应用的支持。
相信通过学习本文,你一定对优雅上下线有了更多的了解。
除此之外,我还希望你,通过本文以后,遇到一些实际问题的时候,可以想到文中提到的shutdown hook机制、Spring的event机制。很多时候,这些机制都能帮助我们解决很多问题。
我在工作中,就有很多次使用过这样的机制的实例,后面有机会给大家介绍几个实例。
好了,本文的全部内容就是这么多啦,如果对你有帮助,记得一键三连哦~
参考 :
https://zhuanlan.zhihu.com/p/29093407
https://www.cnkirito.moe/dubbo-gracefully-shutdown/
关于作者:Hollis(ID:hollischuang),一个对Coding有着独特追求的人,现任阿里巴巴技术专家,个人技术博主,技术文章全网阅读量数千万,《程序员的三门课》联合作者。
相关推荐
- Docker篇(二):Docker实战,命令解析
-
大家好,我是杰哥上周我们通过几个问题,让大家对于Docker有了一个全局的认识。然而,说跟练往往是两个概念。从学习的角度来说,理论知识的学习,往往只是第一步,只有经过实战,才能真正掌握一门技术所以,本...
- docker学习笔记——安装和基本操作
-
今天学习了docker的基本知识,记录一下docker的安装步骤和基本命令(以CentOS7.x为例)一、安装docker的步骤:1.yuminstall-yyum-utils2.yum-con...
- 不可错过的Docker完整笔记(dockerhib)
-
简介一、Docker简介Docker是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源。Docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,...
- 扔掉运营商的 IPTV 机顶盒,全屋全设备畅看 IPTV!
-
其实现在看电视节目的需求确实大大降低了,折腾也只是为了单纯的让它实现,享受这个过程带来的快乐而已,哈哈!预期构想家里所有设备直接接入网络随时接收并播放IPTV直播(电信点播的节目不是太多,但好在非常稳...
- 第五节 Docker 入门实践:从 Hello World 到容器操作
-
一、Docker容器基础运行(一)单次命令执行通过dockerrun命令可以直接在容器中执行指定命令,这是体验Docker最快捷的方式:#在ubuntu:15.10容器中执行ech...
- 替代Docker build的Buildah简单介绍
-
Buildah是用于通过较低级别的coreutils接口构建OCI兼容镜像的工具。与Podman相似,Buildah不依赖于Docker或CRI-O之类的守护程序,并且不需要root特权。Builda...
- Docker 命令大全(docker命令大全记录表)
-
容器生命周期管理run-创建并启动一个新的容器。start/stop/restart-这些命令主要用于启动、停止和重启容器。kill-立即终止一个或多个正在运行的容器rm-于删除一个或...
- docker常用指令及安装rabbitMQ(docker安装rabbitmq配置环境)
-
一、docker常用指令启动docker:systemctlstartdocker停止docker:systemctlstopdocker重启docker:systemctlrestart...
- 使用Docker快速部署Storm环境(docker部署confluence)
-
Storm的部署虽然不是特别麻烦,但是在生产环境中,为了提高部署效率,方便管理维护,使用Docker来统一管理部署是一个不错的选择。下面是我开源的一个新的项目,一个配置好了storm与mono环境的D...
- Docker Desktop安装使用指南:零基础教程
-
在之前的文章中,我多次提到使用Docker来安装各类软件,尤其是开源软件应用。鉴于不少读者对此有需求,我决定专门制作一期关于Docker安装与使用的详细教程。我主要以Macbook(Mac平台)为例进...
- Linux如何成功地离线安装docker(linux离线安装httpd)
-
系统环境:Redhat7.2和Centos7.4实测成功近期因项目需要用docker,所以记录一些相关知识,由于生产环境是不能直接连接互联网,尝试在linux中离线安装docker。步骤1.下载...
- Docker 类面试题(常见问题)(docker面试题目)
-
Docker常见问题汇总镜像相关1、如何批量清理临时镜像文件?可以使用sudodockerrmi$(sudodockerimages-q-fdanging=true)命令2、如何查看...
- 面试官:你知道Dubbo怎么优雅上下线的吗?你:优雅上下线是啥?
-
最近无论是校招还是社招,都进行的如火如荼,我也承担了很多的面试工作,在一次面试过程中,和候选人聊了一些关于Dubbo的知识。Dubbo是一个比较著名的RPC框架,很多人对于他的一些网络通信、通信协议、...
- 【Docker 新手入门指南】第五章:Hello Word
-
适合人群:完全零基础新手|学习目标:30分钟掌握Docker核心操作一、准备工作:先确认是否安装成功打开终端(Windows用户用PowerShell或GitBash),输入:docker--...
- 松勤软件测试:详解Docker,如何用portainer管理Docker容器
-
镜像管理搜索镜像dockersearch镜像名称拉取镜像dockerpullname[:tag]列出镜像dockerimages删除镜像dockerrmiimage名称或id删除...
- 一周热门
- 最近发表
-
- Docker篇(二):Docker实战,命令解析
- docker学习笔记——安装和基本操作
- 不可错过的Docker完整笔记(dockerhib)
- 扔掉运营商的 IPTV 机顶盒,全屋全设备畅看 IPTV!
- 第五节 Docker 入门实践:从 Hello World 到容器操作
- 替代Docker build的Buildah简单介绍
- Docker 命令大全(docker命令大全记录表)
- docker常用指令及安装rabbitMQ(docker安装rabbitmq配置环境)
- 使用Docker快速部署Storm环境(docker部署confluence)
- Docker Desktop安装使用指南:零基础教程
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)