Python twisted事件循环服务更新重启 内存变量持久化、自动恢复的奥秘
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第23天,点击查看活动详情。
背景
我开发过《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋、象棋等游戏。
它是一个WebSocket服务。任何一个需要更新迭代的后台服务,都需要发版。对于个人开发者而言,中断老的服务,启动新的服务,是最方便的操作。我的服务,便是这样操作更新的。
但是我的服务有个特点,当初为了降低系统复杂性,我没有用任何存储组件,所有数据都保存在内存中。一旦我重启服务,所有数据(包括房间数据、对局数据等)全都丢失了。
所以我需要在结束老的服务时,将内存变量存下来,持久化存到文件里。启动新的服务时,再去读取文件,恢复那些数据到变量中。
大家可以看到,我的网页从3月开始,持续了半年之久,数据一直没有异常丢失过。难道是我从没更新过后台服务吗?当然不是。我更新过多次。我就是通过上述「持久化内存变量」的方法(再加上运气好,程序没有异常中断过),才实现了这一目标。今天给大家分享一下。
技术选型
给大家回顾下我的技术选型:
- Python
- Daphne(一个很小的Python的库,一两千行代码,由Django团队开发和维护)
没了。整体是基于事件循环的,在Daphne内部,事件循环逻辑用到了twisted
这个库的一些功能。
解决方案
核心问题
在我们退出 daphne 服务时,如何去执行一段代码,在这段代码中可以将内存变量数据保存到硬盘文件里。
解决思路一:行不通
Daphne 是一个实现了 ASGI 标准的库。
在 ASGI 标准中,有明确定义,需要实现应用的生命周期,包括:启动、结束等。
我可以在应用的生命周期的「结束」阶段,执行我需要做的事情。
但不幸的是,Daphne 开发者恰好没有实现这个生命周期(因为 Django 用不到这个功能)。
他们还邀请我去协助开发实现,但是我哪会呢?😂 我都没阅读过twisted
完整文档和任何源码,估计短时间内没办法帮他们开发出来。我还是专注于解决我面对的问题吧。
解决思路二:行通了
我阅读了 daphne 的源码,发现它有个这样的逻辑:在程序退出时,它需要主动 kill 掉所有客户端的 websocket 连接。这个功能的执行时机,跟我期望的「内存变量持久化」的时机是一模一样的。
其中关键的逻辑就是图中这一行,用到了reactor
,是通过twisted
库导入的:
from twisted.internet import reactor
这,就是秘诀。
具体开发实现
我在我的server.py
(项目的入口文件)加了这样的代码:
from init import init, before_shutdown
init()
reactor.addSystemEventTrigger("before", "shutdown", before_shutdown)
# ...
async def application(scope, receive, send):
# ...
你也一定看得懂了:
- 在新启动服务时,执行
init
函数,就是为了判断文件是否存在,若存在,则读取文件并解析到内存变量中。 - 此外,还注册了一个
before_shutdown
函数,用于事件循环结束时执行,会在那时候把内存变量的内容持久化存储到文件中。
写在最后
我是HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,联系我,交个朋友),转发本文前需获得作者HullQin授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋、象棋等游戏,不收费无广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加Game Jam 2022。喜欢可以关注我噢~我有空了会分享做游戏的相关技术,会在这2个专栏里分享:《教你做小游戏》、《极致用户体验》。
转载自:https://juejin.cn/post/7157737570724806664