基于lua的一种简化数据修改业务的方法

前言:这两年因为专心于制作人岗位,加上之前的项目也不使用lua,所以没咋更新文章了。最近新项目启动,后端人员储备不够,又暂时重回一线(一天都醉心于coding的感觉还是很爽的),主导使用了云风的skynet框架,熟悉的感觉又回来了,对的,lua,哈哈。

我们项目目前对于后端的逻辑业务并不复杂,主要还是用于处理角色数据的业务逻辑,因为就是为了防止客户端修改作弊的行为(上一个项目饱受破解之苦,所以新项目直接按网游模式来做了,虽然主要玩法还是单机思路)。后端逻辑其实大部分时候都是在做数据验证,但数据修改也不可能都是一次性修改,很多时候得分成多步执行,中途如果有操作失败,就需要把之前修改过的所有数据回滚(比如购买类行为,扣除了资源,但后续发放失败),也就是要保证操作的原子性。同时修改完内存数据后,还得通知DB做相应修改的UPDATE,为了简化这个行为,于是设计了一套专门用于修改数据的数据结构,我给它取名为ShadowTable(简称ST)。

ST的原理大概就是每次我们收到业务请求时,针对该玩家在内存中的数据(源数据)生成一个ST,ST本身是一个空表加一个操作记录表,但可以通过元表的__index来访问源数据,玩家对ST的任意修改并不会修改源数据本身,但可供下一次访问使用,同时自身提供一个modify的操作用来显式修改数据,这个地方的数据修改才会记录到操作表中。最终如果所有操作全部完成,则通过一个accept操作将所有modify过的数据同步修改到源数据上,同时生成UPDATE表通知DB做持久化,然后丢弃这个ST。反之,则直接丢弃这个ST,源数据不会发生任何修改。

在实现过程中会有一些细节要处理:

  1. 如果根据key访问的源数据value本身也是一个表,不能直接返回value,需要再次为这个子表也生成一个ST,同时还要继承父ST的操作记录表,这样对这个子ST的所有操作依然会记录下来。

  2. 操作记录表通过闭包形式访问,使用者在使用过程中完全不用关心,使其完全透明。

  3. 对于删除和替换操作,定义了一组关键字用于标识,比如删除行为并不是直接删除,而是对把这个key对应的value设置为一个删除标记,在最终accept的时候将源数据里的key对应的value删除,在访问时候判空也需要增加对关键字的处理。

  4. 为了提高访问效率,避免每次访问都调用__index,对于ST中不存在的key,通过__index访问一次后,直接写入ST中。因为不调用modify,所以它们不会被记入操作记录,对结果不会构成影响。

使用ST后,整个业务逻辑编写起来都变简单了,处理业务时不用再考虑数据回滚问题,出错就直接返回错误即可(上层逻辑发现错误,直接丢弃ST,终止操作,通知客户端结果即可)。对于需要持久化的数据修改也仅需要显式调用modify接口即可。在处理过程中如果模块间有数据需要传递,也可以通过直接写入ST的方式(直接写入的数据最终不会持久化,可视之为临时数据),避免函数的调用参数冗长,同时也可以使各个逻辑处理模块保持无状态行为,最终整个数据处理业务都变得非常简洁。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!