Python游戏服务器开发日记(五)skynet_messagequeue和skynet_timer

        最近,skynet群里有同好与我一起讨论skynet底层一些比较难理解的实现细节,乘此机会仔细看了一下源码,我最关心的是skynet_mq和skynet_timer两个我会用到的部分。


        本文不想讨论太细节的问题,具体的实现思路我并没有理的非常清晰,还是看代码为好。我这里从实际需求出发,写一些tips,方便新来的小伙伴参考。


        skynet的timer和mq,解耦做的非常好,写一个test.c,将它们单独编译,即可单独测试这两个模块,调用头文件里提供的api,可以加深理解。我是直接把它们放到了我的工程里:)


------------ skynet_timer ---------------
        skynet的timer,是单独启动了一个线程,不断的调用tick,检查下一个需要被触发的timer。
        timer的结构体,包含一个near数组和t[4]二维数组,要注意他俩的长度也不一样。。。处理的时候比较繁杂。
        分成near和t两个数组,其实是为了优化。系统time是毫秒级的,每个node里保存的都是绝对时间,根据绝对时间离现在的远近,放在不同的数组里。
        每次只需要查询near里的timer,比较快速。
        判断时间远近的算法都是借用位运算,符合云风的风格。对C的二进制操作不熟悉的朋友,可能会略难懂。
        
        明白了以上几点,如果看清了timer每个函数的意思,还是比较好理解的。剩下一个难点就是时间的回绕问题。
        回绕的原因是用毫秒级计数器保存时间,int32只能保存几十天的长度,计数器必然会在服务器工作时从0xffffffff回到0,skynet_timer的处理比较奇葩,在add_node的时候,没有做任何特殊处理,导致糊里糊涂的把node加入了队列。
        分析的关键一点就破——所有可能回绕的node(时间不要过于太远的),都被阴差阳错的放在了t[3][0]里面。
        然后检测到当前时间点回绕的时候,只要把t[3][0]里的node全都拿出来重新插入一遍即可。这个方法比较trick,反正结果正确就行。


------------- skynet_mq -----------------
        skynet的message_queue,是global_queue和service私有queue的结合体。


        有了service私有queue,每个service同时就只能处理一个消息!这是重点,这是天然的锁,在整个架构中,其实这是一个重点。
        它保证了同一个service不会出现并发。
        还是强调一下上面这点,我设计自己的系统时老忘 :)


        global_queue是挂了一串mq,service处理消息时,把自己的mq取下来,处理完再挂回去。可以避免锁的使用。
        其实后来,global_queue的处理还是加上了锁(貌似叫自旋锁?),因为其实skynet较早的完全无锁实现,并不能通过某些仔细设计的单元测试(这一点我是从github的上传记录里看到的)。问了云风本人,云大表示至少现在的逻辑更清晰了。


------------ 理解context和session -----------
        context 是service执行内容的上下文,貌似一个service只有一个context。
        session是一个整数,用于管理coroutine。lua的coroutine本人非常不熟,这块也无法借鉴。好在python的greenlet我认为更方便使用,greenlet的 C API 也很清晰,很容易和messagequeue嵌入到一起。
        每个context或者说每个service,每当调用skynet.call或者skynet.sleep等导致挂起的函数时,都会产生一个coroutine,分配一个新的session号(简单+1即可),等callback的时候,用这个session号找回原来的coroutine继续执行。
        (这块留空,我还要再仔细确认一下,继续完善)

你可能感兴趣的:(编程,python,大作)