skynet_fly热更新存在的问题
前言
最近有空在用skynet_fly写中国象棋的游戏,发现了room_game房间类游戏基础架构有些许不够好的地方,就对room_game房间类游戏基础架构做了优化,具体优化有空会另外写一篇blog,这里主要记录一个我在优化的时候,突然想到的一个热更问题。
问题描述
触发场景分析
假如有A、B 两个服务,代码如下:
- A服务
1 |
|
- B服务
1 |
|
很简单的一个示例,A服务间隔1秒给B服务发送hello消息。
然后我们修改代码如下:
- A服务
1 |
|
- B服务
1 |
|
再执行热更,我们修改了A,B服务,我们不想让AB服务继续发送消息了,所有直接B服务直接去掉了hello命令,A服务也去掉了向B服务发送消息的代码。此时看起来没什么问题。
这时就会有一个问题,旧的A服务监听到了B服务更新了,此时它的消息会发送到新的B服务,那么问题就出现了。此时新的B服务hello函数并不存在。
解决方案的思考
- 方案一
目前能想到的是,当一个服务是旧版本时,不能去切换访问新的服务,这种方案要保证旧服务需要联系的其他旧服务暂时都不下线。还有就是必须所有服务热更之前就要关闭掉该服务的新服务切换。
深入思考出现的问题
热更过程中,假设A服务先热更,A服务有可能访问到旧的B服务。也是有问题的。
解决方案
要解决这个问题就必须保证所有服务热更完之前,对服务id的查询暂时先阻塞。
解决方案是contriner_mgr的所有cmd命令用queue队列包裹起来,这样load启动完所有需要加载的热更模块之前所有query都得先排队,不过这样需要去避免环队列问题,load和start过程中不能有服务去调用query。所以在start完之前,需要把contriner_client:new命令暂时禁用掉。
如果是非可热更模块服务就没有这个限制。环队列问题
假设队列里面有A,B两个函数,队列执行顺序为B>A>
,也就是先执行A,再执行B。
前提是B的函数执行是因为A函数的call调用发起的。
此时就会出现A在等B的回应,B在等A先跑完。
做了上诉解决方案的修改后,解决了新旧版本串调的问题,此时又多了一个问题。
旧服务何时销毁退出
之前旧服务被新服务顶替之后,就可以确定自己不再会新的访问者了,现在是可能存在访问者,不能再像之前一样只需要判断自己还有没有需要处理的数据。
解决方案
记录来访者地址,检查是否可以退出时,询问来访者是否不需要访问自己了或者已经退出了,当所有来访者都退出了,说明此服务可以退出了。这种处理方式有环问题。边界问题
- 当旧服务可以退出时,会不会出现全新的来访者。
除非手动指定id去调用,不然不会出现全新的访问者。新的query查询都会查到新服务,想到这里又想到一个要退出的旧服务可能访问新服务的问题,迟到访问问题。
- 当旧服务可以退出时,会不会出现全新的来访者。
迟到访问问题
热更之前A服务没有访问过B服务,就是还没有查询过B服务的地址,热更结束之后,旧A服务想访问B了,此时又会查询到新服务。
解决方案
只要用到contriner_client的任何服务,都需要先先注册好需要访问的服务,没有注册的不能访问。环问题
A访问B,B访问A,都退出不了。
引入弱访问者,AB互相联系,应该有一个占主导地位的发起方。主导发起方不用管被访问者是否退出。
总结
此次发现并解决了热更可能存在的诸多问题。