如何安全的退出 skynet
Skynet 在最开始设计的时候是没有仔细考虑系统退出的问题的,后来为了检测内存泄露的问题,加入了一个 ABORT 指令可以杀掉全部活着的服务。
安全的退出整个系统,尤其是一个分布式系统,总是一个复杂的问题。我们的内部版为这些做了大量的工作。但我觉得做的不太干净,所以一直没有把代码合并到开源版。
今天晓靖又重提这个退出系统的方案,就又把这个问题拿出来讨论了一下。我的个人观点是,如何安全的退出和业务逻辑相关性很强,在框架中加入大量的通知机制让每个服务自己协调解决即增加了框架的复杂度,又不降低系统的设计难度。我们目前内部版本中增加的服务退出消息以及服务优先级等等这些机构过于复杂,且没有很好的解决问题。
曾经我想过按操作系统的做法,杀掉一个服务先发送一个消息给这个服务,让服务有一小段时间可以做最好的事务处理。等超时后,就强行把服务杀掉。这样显得不够安全,但若等服务自行干净退出又有可能发生死锁。我觉得,对于整个系统都是自己设计构建的话,其实设计人员是能够理解如何安全的关闭系统的。只有小部分服务需要做善后工作,比如数据库读写层需要把尚未写完的数据写入数据库后才能退出;大部分服务是无状态的,它们可以直接清理;还有一部分服务的关闭过程比较复杂,比如网关,就需要先关闭监听端口,再逐个关闭客户链接,最后才能退出自己。
让每个服务自己收到退出消息后想办法处理好退出流程,不如有一个专门掌管系统关闭的服务来统一协调系统关闭退出的流程。目前的 launcher 服务掌管了大部分服务的启动流程,可以稍加改进来管理退出过程。也可以把这一部分业务独立实现。无论怎样,都不要在框架底层来做太多事情。每个有复杂退出流程的服务,应在启动时把自己上报,退出管理器了解所有系统中活跃的服务,按系统的整体设计来决定退出的时候按怎样的次序做好哪些事情。
框架可以做一点点事情,能让这件事情做起来简洁一点。那就是可以指定一个服务可以监听到其它服务的关闭事件,而不需要服务在退出的时候自行汇报。
我没有在框架层把这个服务退出机制设计成 Erlang 那样的 process link 的方式是因为,n:m 的 process link 会使得管理连接的模块实现的过于复杂。用单点接收所有的服务退出消息,同样也可以在这个单点上进一步实现出 Erlang 那样的 process link 机制来。
目前的接口是放在 skynet.lua 里的 skynet.monitor 方法。在启动脚本里调用它启动一个监听服务。我编写了一个简单的监听服务 simplemonitor.lua ,在每个服务退出时输出一行 log 。用户可以改进这个服务做更多的事情。
Comments
Posted by: 张龙琪 | (7) December 26, 2013 11:32 AM
Posted by: Cloud | (6) August 13, 2013 07:59 PM
Posted by: Soli | (5) August 13, 2013 03:39 PM
Posted by: alex | (4) August 8, 2013 02:10 PM
Posted by: wstc | (3) August 8, 2013 09:45 AM
Posted by: qiaojie | (2) August 7, 2013 01:01 AM
Posted by: 杨博 | (1) August 6, 2013 06:28 PM