likes
comments
collection
share

Python twisted事件循环服务更新重启 内存变量持久化、自动恢复的奥秘

作者站长头像
站长
· 阅读数 13

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第23天,点击查看活动详情

背景

我开发过《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋象棋等游戏。

它是一个WebSocket服务。任何一个需要更新迭代的后台服务,都需要发版。对于个人开发者而言,中断老的服务,启动新的服务,是最方便的操作。我的服务,便是这样操作更新的。

但是我的服务有个特点,当初为了降低系统复杂性,我没有用任何存储组件,所有数据都保存在内存中。一旦我重启服务,所有数据(包括房间数据、对局数据等)全都丢失了。

所以我需要在结束老的服务时,将内存变量存下来,持久化存到文件里。启动新的服务时,再去读取文件,恢复那些数据到变量中。

大家可以看到,我的网页从3月开始,持续了半年之久,数据一直没有异常丢失过。难道是我从没更新过后台服务吗?当然不是。我更新过多次。我就是通过上述「持久化内存变量」的方法(再加上运气好,程序没有异常中断过),才实现了这一目标。今天给大家分享一下。

技术选型

给大家回顾下我的技术选型:

  • Python
  • Daphne(一个很小的Python的库,一两千行代码,由Django团队开发和维护)

没了。整体是基于事件循环的,在Daphne内部,事件循环逻辑用到了twisted这个库的一些功能。

解决方案

核心问题

在我们退出 daphne 服务时,如何去执行一段代码,在这段代码中可以将内存变量数据保存到硬盘文件里。

解决思路一:行不通

Daphne 是一个实现了 ASGI 标准的库。

在 ASGI 标准中,有明确定义,需要实现应用的生命周期,包括:启动、结束等。

我可以在应用的生命周期的「结束」阶段,执行我需要做的事情。

但不幸的是,Daphne 开发者恰好没有实现这个生命周期(因为 Django 用不到这个功能)。

他们还邀请我去协助开发实现,但是我哪会呢?😂 我都没阅读过twisted完整文档和任何源码,估计短时间内没办法帮他们开发出来。我还是专注于解决我面对的问题吧。

解决思路二:行通了

我阅读了 daphne 的源码,发现它有个这样的逻辑:在程序退出时,它需要主动 kill 掉所有客户端的 websocket 连接。这个功能的执行时机,跟我期望的「内存变量持久化」的时机是一模一样的。

Python twisted事件循环服务更新重启 内存变量持久化、自动恢复的奥秘

其中关键的逻辑就是图中这一行,用到了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个专栏里分享:《教你做小游戏》《极致用户体验》