silly的一次大规模重构

最近一个月都在重构silly, 包括其工作模式以及一些扩展库的实现基本上都被重写了。

其实coding的时间并没有想象中的那么长,只是在重构过程中碰到很多取舍情况,大部分时间都耗费在了纠结的时间上。

当初实现silly的初衷是首先满足类似gameserver这类业务模型的需求,然后尽可能多的兼顾其他类型的server。

然而以这两年的工作经验来看,master-worker模式由于多worker之间不能进行通信,因此仅仅适用于http这种不带有状态残留的协议模型。

而对于gameserver这类在进程内存内残留状态的业务开发并没有什么用。因此这个功能算是鸡肋功能了,犹豫再三最终还是将master-woker模式拿掉,将silly改为单纯的单进程单线程工作模式(当然内部还是有单独的timer/socket线程的).

而以我有限的服务器开发经验来看,在高并发分布式开发过程中, 应该将业务流程抽象成pipeline节点, 然后根据每个pipeline节点的负载情况动态增加或减少相应节点的物理进程。因此拿掉master-worker工作模式之后,准备在多进程通信方面多增加一些支持。

单进程单线程模式并不意味着就无法充分使用多核cpu, 如果服务器性能足够好,则可以尽可能多的把进程布到同一台物理机上,如是服务器性能略差则可以相应减少进程数。采用这种模式可以大大提高服务器的可伸缩性而并不需要业务逻辑做出改变。

另一个比较大的重构就是lua层的socket coroutine调度方面。

在之前的coroutine调度模块中,不管是core.lua中还是其他相应的扩展库中,都是直接使用lua提供的原生coroutine库进行操作的。

这样就会有一个问题,core.lua没有自成一个闭环,也就是说只要其他基础库实现的有bug, 就会扰乱socket coroutine的调度,而且极难查出问题出在哪。而在阅读lua源码的过程中可以看到他的所有api接口都是自成闭环的,换句话说只要在luaconfig.h中把检查参数打开,只要你传入非法值,他都是可以检测到的,这极大的降低了debug的难度。

为了使用socket coroutine的调度也自成闭环,我封装的core.fork/core.wakeup/core.sleep等与coroutine相关的函数,除了core.lua之外,不允许其扩展库使用lua原生的coroutine库。而core中提供的相应的coroutine操作函数在调用时都会去检查和设置相应的coroutine状态。

这样即使某个扩展库对coroutine的操作流程不正常,也只会影响调用这个扩展库的coroutine, 更重要的是一旦这个扩展库出现coroutine调度上的bug, core层lua代码能及时发现问题,并将错误信息抛出,极大的除低了debug的难度。

btw, 个人觉得一个成熟的库,其自身是否为闭环是重要标志之一。

在重构扩展库gate.lua时我犯了一个错误, 这个错误导致我重写了好几遍代码,最后才醒悟过来。

在一开始时,我想让gate尽可能的适用于各种情况, 因此重写了一次又一次总觉得不太满意。 最后我幡然醒悟,满足各种情况的库是不可能做到的。

最后确立了core.lua + C核心代码 为整个silly的核心,其他都是扩展库,而gate.lua仅仅不过是一种对核心代码的特殊情况的使用而已。 到此才停止了对gate.lua不停重构。

由此,我得到一点重要的启示。一个框架一定要明确这个框架的核心要解决哪些问题,然后提供灵活而正交的接口供扩展库使用。扩展库的功能则是拿核心接口采取不同的策略去处理不同的情况。



发表评论