关于skynet_fly热更新实现细节
前言
由于几个月的时间里,断断续续发现一些问题,做了很多调整,与最初的版本已经有些许差异了,于是打算重新梳理一下热更的流程实现的细节。
实现思路
可以先看看这篇关于skynet_fly热更新实现
整体还是围绕容器管理员,容器客户端,可热更服务展开。
职责
- 容器管理员 负责启动,通知关闭,通知更新
- 容器客户端 负责注册访问,监听,服务切换,记录需要访问,回复是否需要访问
- 可热更服务 负责记录来访,检查安全退出
启动流程
容器管理员查看有没有旧服务,有的话通知关闭,然后启动新服务,通知更新(监听的容器客户端)更新地址。
访问流程
容器客户端 在加载代码阶段提供好注册表,在skynet.init启动阶段根据注册表启动多个监控循环进行注册访问,监听地址更新,记录需要访问的地址。访问实例直接引用本地记录地址进行消息发送,根据访问实例提供的can_switch_func
服务切换检查来决定是否进行服务切换。记录需要访问的地址使用弱引用表记录,当没有任何访问实例引用时,说明不在需要访问该地址。当需要退出的可热更服务来询问是否需要访问它是,它就已经不在记录需要访问的表里了(回复是否需要访问)。
退出流程
可热更服务 在被通知关闭后会启动一个定时轮询任务做检查安全退出的流程,检查流程分为两步,1是调用本服务的check_exit
函数,看本服务的工作结束没有,如果结束,再看条件2询问来访者是否还需要访问自己(记录来访的目的),当全部不需要了,确认服务可以退出。
流程图
细节展开
容器管理员
启动
因为热更过程中,假设A服务先热更,A服务有可能访问到旧的B服务(AB问题),所以容器管理员必须保证没有重入问题,我使用了skynet.queue来做保证。
通知关闭
通知旧服务关闭我分为了herald_exit
预告退出,fix_exit
确认退出,exit
退出,check_exit
检查退出,cancel_exit
取消退出5个回调为什么要分这么细,肯定是有原因了,一开始只有一个exit
的回调。
- herald_exit 预告退出 由于有些服务需要退出前做一些处理,比如我实现的web_master_m/web_slave_m模块,web_master_m会监听一个端口,当web_master_m要热更时,旧服务需要先关闭监听端口,让新服务去监听。
- fix_exit 确认退出 收到这个回调这个服务就要开始执行关闭前的一些安全处理了。(要注意,确认退出了,不代表没有新的服务访问你了,可能在watch的间隙拿到你的地址或者有常驻的访问对象)
- cancel_exit取消退出 新服务启动失败了,比如热更web_master_m,调用start函数没有返回true,说明启动失败,这时候旧服务需要继续工作,保证健壮性。
- check_exit 这个是确认退出以后会轮询到exit回调为止,如果当前服务还有任务没有完成,不应该返回true,或者不实现这个回调处理。
- exit退出 表示已经进入退出销毁的倒计时,到这个阶段这个服务不应该还有任务(到这个阶段可以确定没有访问者了,除非你用原生的skynet.call或者send调用)。
通知更新
watch机制能保证容器客户端一直持有最新的地址信息。
容器客户端
- 注册访问 为了解决迟到访问问题,访问服务之前要先注册
contriner_client:register
,确定服务启动好就知道需要访问哪些服务。比较特殊的情况是frpc_server
集群服务,一开始并不知道需要访问哪些服务,就需要按照全部都访问的逻辑处理,但是可能存在服务不重启就新增模块的情况,就需要动态的去扩展,于是就新增了monitor_new
监听新模块的流程,通过调用contriner_client:monitor_all()
就可以监听所有热更服务的地址,用于解决这种情况。
特殊处理 因为普通的skynet服务不参与热更,所以它在什么时候注册访问可热更服务都可以,只是需要避免迟到访问问题,所有在服务启动好后就直接监听所有热更服务地址,就能很好的避免这个问题。
- 监听 通过sub/pub机制保证一直能拿到新的地址。由于成为旧服务的时候,不能去访问新服务,此后不在更新服务地址。
- 服务切换 服务切换由
new
生成的访问实例传入的can_switch_func
控制,不传默认切换。使用者自己考量。 - 记录需要访问 每次拿到服务地址都会用弱引用表记录,当该服务地址没有强引用时,弱引用也会清除。可以很方便的记录管理需要访问的服务。
- 回复是否需要访问 判断是否在记录的访问地址中。
可热更服务
- 记录来访 每个容器客户端都会注册访问,此时记录来访地址。注册为弱访问者的不记录(用与解决出现环访问的情况,应该总有一个占主导地方),应该尽量避免出现环访问。
- 检查安全退出 会启动一个轮询定时器,调用
check_exit
确定可以退出,再询问所有访问者是否还需要访问,直到可以退出并且没有访问者需要访问时就可以安全退出了。
总结
重新梳理了一遍热更流程,目前最新的版本是最健全完善的版本。
skynetfly源码地址