「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」
引子?
关闭?还特么优雅?
实际上优雅关闭还有另外一个名词, 叫“平滑退出”。如果你打算自己造轮子, 优雅关闭将是你要掌握的第一个知识点。
在生活中,如果有一个名词确实过于难以理解,我们不妨来看这个名词的反面是什么。
举个简单的例子
- 优雅关闭: 就是使用操作系统关机功能关闭你的计算机
- 不太优雅的关闭: 直接断电重启
可能有同学会有疑问了: 我平时电脑卡住的时候都是直接断电重启的啊,也没见有啥大问题啊?
计算机与计算机的体质不能一概而论, 如果你的计算机安装的是Linux
系统,恰好这又是一台服务器的话,强制重启的话,你大概率会丢掉部分数据,如果是生产环境的话,那就准备提桶跑路吧。
为什么呢?
如果你曾经想过要做MySQL
或者Redis
的调优, 或多或少接触过以下参数:
MySQL
的sync_binlog
参数, 用来控制持久化binlog数据的存储设备的行为Redis
的appendfysnc
参数, 用来控制Redis
持久化AOF日志到数据存储设备的行为。
出现以上参数的原因是因为将数据持久化到存储设备是一个耗时相对较高的行为, Linux
采取的优化措施是,当你往一个文件中数据时会暂存到系统的缓存中, 等待时机再批量持久化到存储设备中。除非进程指定使用DirectIO
的方式或者调用fsync
,操作系统才会主动将数据写入存储设备。
因此MySQL
和Redis
纷纷开放了调优参数用来控制日志持久化行为,并将锅甩回给了程序员。
sync_binlog
的值一般为1, 即每次提交事务就立即将binlog数据持久化到存储设备。
优雅关闭
现在,从不太优雅关闭的例子了解到优雅关闭要做什么了:
- 让程序完成未完成的工作(如:提交事务, 持久化日志等等)
但是,我们还需要加一个限制条件:
- 当程序决定优雅关闭的时候,就不能再接送任何请求。
不停止处理新请求的话就永远没完没了
线程池的优雅关闭
线程池(ThreadPoolExecutor
)在JDK
的并发包中占据了重要的位置, 我们可以来看看如此重要的一个基础组件是如何处理优雅关闭的。
该类将是否需要优雅关闭的权限开放给程序员, 并提供了两个方法,分别是:
ThreadPoolExecutor.shutdown
该方法会将线程池的状态设置为SHUTDOWN
, 并且不再接受新任务的提交,但会让线程池中的线程跑完所有已提交的任务ThreadPoolExecutor.shutdownNow
该方法会将线程池的状态设置为STOP
, 并且不再接受新任务的提交, 以及立即向线程池中的所有线程发出中断信号,对于使已提交到线程池中但还未运行的任务直接忽视掉。
优雅关闭进程
如何优雅关闭进程呢? 首先我们需要搞清楚进程什么情况下会关闭:
- 主动关闭(对于一个对外提供服务的进程来说通常不会主动关闭)
- 程序崩溃, 如某个业务抛出异常处理不当,导致异常抛到最外层并且没有进行处理导致程序崩溃
- 进程收到来自操作系统的关闭信号(如按下Ctrl+C)
在企业级应用中,一个进程通常不止有业务逻辑,还有围绕着业务而开发的日志服务/MQ服务/运维服务等等, 那么当某个业务出现可能导致进程崩溃的问题时,我们就需要将进程即将关闭的消息广播给其他服务, 并调用这些服务提供的优雅关闭方法, 以上措施全部完成后再退出进程, 如日志服务的优雅关闭是确保日志落盘, MQ服务的优雅关闭是确保消息被投递出去或者被消费完等等。
如果你是直接杀进程(kill -9)的话, 也就没有必要讨论优雅关闭了
我们以Golang为例来描述如何优雅关闭进程, 首先我们需要对进程中的服务做一个抽象,以便实现生命周期管理, 每个服务提供均需要提供Serve
和Shutdown
方法。
1 | go复制代码type Service interface { |
接下来我们定义一个ServiceGroup
用来管理Service
生命周期, 当任意Service
运行出错或接收系统信号SIGINT
(Ctrl+C触发)和SIGTREM
(kill 不加参数), ServiceGroup
将负责关闭关闭由此管理的Service
并调用Shutdown
方法。
1 | go复制代码type ServiceGroup struct { |
接下来,我们定义一个会随机panic
的业务服务以及日志服务。
1 | go复制代码type BusinessService struct { |
运行
1 | go复制代码func main() { |
运行输出如下所示:
以上代码还有诸多优化的地方, 读者可自行改进。如可以使用errorgroup
对服务进行管理, 以及Shutdwon
的时候也可传入上下文做超时管理。
总结
什么是优雅关闭?
- 让程序完成已提交但未完成的工作
- 不再接收新的请求
本文转载自: 掘金