« 开发笔记(15) : 热更新 | 返回首页 | Ringbuffer 范例 »

开发笔记(16) : Timer 和异步事件

这几天,安排新来的王同学做数据持久化工作。一开始他是将 sharedb 里的数据序列化为文本储存的。这步工作做完后,开始动手把数据放到 Redis 数据库中。我们的系统主干由 Lua 构建,所以需要一个 Lua 的 Redis 库。google 来的那份,王同学不满意。三下五除二自己重写了一个。据说把代码量减少到了原来的三分之一(开源计划我正在督促)。唯一的问题是,如果直接采用系统的 socket 库,不能很好的嵌入我们的整个通讯框架。我们的 skynet 全部是通过异步 IO 自己调度的,如果这个数据服务单方面阻塞了进程,会使得别的进程获得不了时间片。

蜗牛同学打算改进 skynet 增加异步 IO 的支持。

我今天在考虑现有的 API 时候,对比原有的 timer 接口和打算新增加的异步 IO 接口,发现它们其实是同一类东西。即,都是一个异步事件。由客户准备好一类请求,绑定一个 session id 。当这个事件发生后,skynet 将这个 session id 推送回来,通知这个事件已经发生。

在用户编写的代码的执行序上,异步 IO 和 RPC 调用一样,虽然底层通过消息驱动回调机制转了一大圈,但主干上的逻辑执行次序是连续的。

受历史影响,我之前在封装 Timer 的时候,受到历史经验的影响,简单的做了个 lua 内 callback 的封装。今天仔细考虑后发现,我们整个系统不应该存在任何显式的回调机制。正确的接口应该保持和异步 IO 一致:

每个独立服务有一组信息包的分发器,外部来的消息会被并发的处理,每条消息是一个独立的执行序,相互不会被阻塞。同时,服务本身有一个主干执行流程,在启动之初就开始执行,可以认为它响应了一个启动消息。btw, 启动消息同时可以看成是热更新的重启消息。

无论是主干执行序还是其它消息的响应函数,它们都可以在里面调用一个叫 sleep 的函数。这其实就是用底层的 timer 回调实现的。调用的时候,当前执行序会被调度器挂起,直到 skynet 计量指定的时间长度后,重新从这个断点继续执行。

这样做,隐藏了 timer callback 的细节,隐藏了异步性。用户也很难主动的并发出多条并行的执行序来,可以减少系统复杂性。

热更新该怎样做呢?

我希望是在更更新时,收到热更新消息后,只是在系统里设置了一个标记。然后在主干代码中,合适的位置去检查这个标记位,体面的结束。这比较像 C 语言中处理中断信号的手法。比简单粗暴的杀掉注册的 timer 更为合理。


ps. 前两天碰到了服务相互依赖性的问题。需要主动在启动流程中提示,依赖哪个服务先启动。也就是有个服务启动管理器的服务,响应服务启动的消息,以及提供 RPC 调用,可以在一个服务启动后,返回成功信号。

Comments

异步RPC的确是考验人的事, 要是和signal /slot 机制结合起来会怎样?

用coroutine隐藏回调,会造成用户不清楚程序执行流程,造成隐藏的bug

博客不错 http://www.sinogol.com/前来支持~

第一次留言

第一次留言

建议看下libevent的实现。相当漂亮。你们要的异步IO和timer都可以参考。

看到p.s.,突然觉得你们在写一个OS了……和upstart很像……

“我们整个系统不应该存在任何显式的回调机制”

这是我的梦想啊

好像不能完全解决异步带来的时序问题。==>用户也很难主动的并发出多条并行的执行序来,可以减少系统复杂性。
于QC而言,其实该做的检查和判断还是需要做的,写逻辑的人,防御式编程的思想还是要有的。

开源开源。- -#

要是云风的大部分代码都开源了 我们就爽歪歪了

timer 游戏频繁使用的, 看看云风怎么实现的

你这这篇开发笔记描述的场景和我最近开发的业务很像。我们的平台是一个事件处理模式,可以实现同步和异步编程的效果,而且大量的使用了Timer。

前几天组内讨论时发现,业务结果的发放需要异步,即第三方请求过来,我们直接告诉他已开始处理,等业务处理好会回调第三方接口告诉它处理结果,这样就需要第三方请求时带一个id过来,处理完我们把id一同推回给他。

是不是和你们的场景很像??

确实都异步事件。前半文有共鸣,后半文没来理解楼主的意思。

架构设计方面的东西太考验人了

看不懂啊,风哥。

云风timer会用linux内核那套吗?挂接在数组上

热更新难道不是用lisp之类的更方便么?

坐回沙发吧。

支持一下,感觉你们才是在做hacker的事情啊,不像我们码农,哈哈

Post a comment

非这个主题相关的留言请到:留言本