Contents

工作总结(2022)–如何正确重启游戏服务以及流量管理的必要

昨天突然被告知公司的项目上线运营的这段时间,数据反馈不好没法大推,公司无奈只能将我所在的项目组给停掉。于是我顺利的被迫失业了。虽然有挺多不甘的,但是在这一年的工作中我还是学习和实践了很多的东西。在我重新收拾好自己出去找工作之前,先对今年的工作进行一系列的总结吧。这些总结来自我在项目中实践得来的感悟,可能会有很多的主观看法。

在今年的工作中,随着毕业后经验的积累,我对公司的项目是有一些想提出来的。但是碍于我的资历太浅(另外两个同事都是有家室的人了, 资历比我深很多),有一些意见可能会带来冲突,所以我选择藏在心里。那么离职之后,我就会想通过博客将自己的一些优化的意见反思总结,然后写成博客的形式进行总结。当然也会包括一些我敢于提出并且被采纳的意见的总结。

同一系列的还有我在写业务的时候的一些心得,还有关于项目管理上面的一些意见。今年干的活除了写业务,还涉及到一些关于运维上面的工作。总的来说干得活会比较杂,看的代码也很多,看的多,自然就会发现一些问题。

这篇文章来自于我在为公司写一个发版工具的时候,在服务器重启流程上的一个疏漏,这个疏漏引发了我对如何正确重启服务的思考。

起源: 一个发版工具

因为公司的项目上线之后,管理的服务器也许不是一台两台的事情,每次更新版本还需要手动ssh上去重启的话就是一件不合理的事情了(公司第一次做在线游戏,这方面的流程还处在摸索阶段)。所以我的老大找到我让我给他写一个发版的工具,要求能够帮助他分发配置文件,和重启服务器。而问题也就出在重启服务器的流程上。

虽然我对重启服务器的流程已经做了思考,但是还是忽略了一些东西。后端的项目在结束进程上是不规范的,结束进程是直接使用 kill -9 的杀无赦策略。这意味着结束进程的时候,不会给进程任何的反应机会直接将进程杀掉。

而进程中的业务在不知情的情况下,还在对数据库进行操作。那么这样的话就可能会导致数据库的数据会出现问题。假设我有一个业务操作要分别对数据库进行两次写入操作,这两次操作我分别称作A 和 B。那么在A 执行的过后,进程被杀掉B没有被执行,这个时候这个业务操作就会陷入只执行了一半的尴尬情况。

另外,强制杀死进程还会导致数据库的链接没有关闭,无端占用数据库链接的问题。

如何解决这个问题

对于这个问题的解决,简单但是不严谨的方式是,先把gateway这样的流量代理服务给停掉,然后后等一会在把游戏的业务服务器给关掉。之所以说简单,因为这种方法确实简单,之所以说不严谨,就是这个 等一会 ,到底是等多久。而且这种方式还是没法解决数据库链接被占用的问题。

在没有对服务内部进行监控的情况下,你根本不可能精确的知道服务内部到底是一个怎样的状态。这个 等一会 ,你就只能是靠猜测。所以1.我们需要对服务内部的流量进行监控,2.进程的结束要更加规范化。

规范的结束进程

上面提到 kill -9 属于杀无赦的结束进程方式,只能在迫不得已的情况下才能使用。根据posix的规范,其实还有别的级别比较高的终止进程的信号比如 term

强制杀死进程是不会给程序反应机会的,停止进程应该是通知进程我想让你停止,请你做自己做好内部状态的维护然后自己退出进程。两者的区别在于,前者不会留给进程反应机会,而后者会。在这个反应的时间内,进程可以检测内部的状态,直到状态符合推出进程的要求时,再退出进程。这样才能对流程的控制力才会更强,减少了灰色地带出现的可能性。

流量控制

这里的流量控制并不是gateway层面的流量控制。而是业务服务器对自身内部正在执行的业务的流量控制。划分原则是:进入gateway 但是还没进业务服务器,我们就叫gateway层面的流量控制,而流量一旦进入业务服务器了,我们就叫业务层流量控制。

业务层的流量控制我们需要关心的是这个业务流量对应的生命周期。在流量进入业务服务器,我们说这个业务开始了,流量对应的业务执行完后,我们说这个业务的生命结束我们消耗了一条流量。

当然这种对业务流量的监控可能会导致一些性能上的退化,所以我建议使用队列,单独开一个线程去处理,只要不阻塞业务逻辑线程就行。具体实现方式的话,你可以用一个hashmap去存储进入的流量,想流量的key传递下去,等业务处理完毕,将key push到管理流量消费队列里面去,让消费队列的线程异步去将流量关闭就好。实现其实可以很多样和优雅,这也是编程的魅力所在。

关服流程

有了流量控制,我们就知道了当前服务器还有多少的逻辑没有处理完毕。这时,我们就可以配合term信号去处理关服了。流程如下图。

总结

其实要点在于,服务内部状态要做到心中有数。程序自身应该明确知道在某种状态下可不可以退出进程。严格禁止使用 -9 去停止进程除非迫不得已。