Contents

GoGameActor-102 hotfix introduction

上周在思考框架该不该上热更,以及如何适配热更的时候(其实一行代码都还没写呢😕),迫使我不断去思考如何去热更才能做到正确。但是,热更这个事情即使是lua,也需要加上很多的限制。而且,热更这件事情不但和语言层面相关联,和框架的设计也有很密切的关系。最近在网上看了很多博客,所以这里做个思考记录。

什么是热更

热更就是在不中断服务的情况下(注意是不中断服务而不是不重启进程或服务器),对程序的行为进行修改。简单来说,如果已经部署的代码执行的逻辑是1+1=2, 那么热更就可以改变1+1=2这个行为,比如该更1+1=3,并且对外的服务是不能中断的(因为用户会无法访问业务)。在用户无感知的情况下,改变原有的代码逻辑,我们也可以这么说。

为什么要有热更

对于互联网行业来说,有流量才有可能变现。流量意味着有人在使用产品,有人使用产品我们才能着手诱导用户进行消费。试想,一个酒楼没有一个顾客(流量),他的酒,服务,饭菜要如何销售变现?而对于互联网产品来说,一秒钟的流量就可能高达千万,而这一秒的流量转化,即便只有1%,也是很多的。

试想一下,现在这一秒有1千万的流量,转化率是1%,那就意味着这一秒中有100个人在下单,假设此时一单的流水是1块,那这一秒钟的流水就是100块。 注意这里只是这1秒钟,如果时间拉长到1小时,12小时,24小时。乞丐都会知道要是每秒有人给自己扔100块,一天自己就可以🏠靠海了。

如果这时停机维护1小时,要损失多少钱?这大概就是为什么互联网行业都非常忌讳停机维护,因为"时间"约等于"流量"约等于"钱"。而对于游戏这种纯靠流量来赚钱的行业更是如此了,不停机维护已经是游戏行业的标配了。

热更的方案

  1. 服务器无状态,快速重启。这种方案的痛点是,服务器的重启必须尽可能的快。慢了,客户端就要考虑重登和重发协议。而且由于无状态,业务的编写更倾向于web那一套。

    服务器不好维护用户状态,业务的性能和编码的难易程度一得看项目是如何架构的,二得看有没有有意识的迭代代码。如果两者都做不到,性能就由数据库决定(不做cache的话),编码就有可能要不断处理操作数据库所有可能带来的异常。因为无状态是不可能将业务的状态存储在内存的,多数是直接入库或者入cache。

    对于go来说,你得时刻注意操作数据库时抛出来的error,如果不能从框架上先行对这种error进行过滤,会因为频繁的写重复代码而控制不住的觉得烦。比如,很多rpc的接口都需要用到usr这张表的信息,如果不能在rpc来的时候,自动去获取usr,那就需要程序员自己去编写。程序员的时间就浪费在了这种事情上面,未免有些亏。但是,又不是每个rpc都要用到usr表的信息,如果每个都去查一遍usr表,又会浪费数据库那有限的吞吐。所以做无状态,架构代码的人必须足够牛🍺,项目成员要时刻有迭代代码的意识。有的时候项目老大还要在编码和性能之间做权衡。

  2. 服务器无状态,将新的流量导入新的进程或者新的服务器。这种方案得在router层做好流量的转发,而且如果有些逻辑是有状态的话,还得做好状态的移交(虽是无状态服务器,但是有的逻辑有状态是难免的)。

  3. 服务器无状态,直接将代码打进正在运行的进程中,这种就比较难了。需要工程师足够了解语言特性,和框架,知道如何去替换运行时的函数。

  4. 服务器有状态,直接将代码打进正在运行的进程,这个难度比三要大。而且由于有状态,不但得做到3提到的全部,而且得做到运行时状态的转化,热更代码的编写会异常的复杂。一般来说这种方案都会在架构上加上各种限制来保证热更的正确性,所以缺点就是有各种限制。比如逻辑和数据必须严格分离,最好只热更纯函数, 对于弱类型语言会好做一些比如(lua,js)这种,允许数据结构修改的同时去更改逻辑函数做到热更正确,但是对于强类型语言(go,c++)当需要修改数据类型的时候可能不得不重启服务器,而且可能会带来动态链接模块版本控制等一系列问题(我的框架可能会选择这种方式)。

  5. 只热更策划表,这种就比较容易了,做好所有业务逻辑从统一的地方拿配置就行。对于静态语言来说,配表结构更改还是免不了需要重启服务器。编码的时候预留控制台接口,需要更新时由控制台发送reload配置的命令,程序先锁住配表服务然后直接reload所有的配表再解锁就可以了。

总结

静态语言虽然性能上要好过动态语言,但是灵活性上大大弱于动态语言。无状态虽然能做到无感知,但是吃重启速度,还得客户端配合,想要性能和愉快的编码,你得找好点的搭档,架构得好好设计别偷懒。

热更会带来很多的限制,引入新的特性,新的技术都会带来新的副作用和问题。引入之前必须做好权衡,想好了再做。框架编写非常吃架构师的能力,你得给他足够的时间去思考架构上的问题。

我就遇到无状态服务器频繁操作数据库(因为当时后端环境非常恶劣,只有他一个人,他来的时候没有框架,还得尽快对接业务,后端1v8非常惨烈,而且说好一个服只有4000的导量,所以没有上cache),老大担心性能上会遇到瓶颈, 所以让我去做一些热表(数据库中需要频繁访问的表)的cache。但是我做完了热表的cache后,发现热表的cache会影响一些跨服的业务。这个时候修改跨服的代码代价会比较大,而且也没到非上不可的时候(导量还是不会突破4000的)所以这个事情只能暂时搁置, 我还得去写别的业务。但是就怕有一天性能会遇到瓶颈,遇到了我那写了挺多跨服业务的同事就得忙一会了,我最近也写了一些,我也得忙一下了😂。