« 增强了 skynet 的 socket 库 | 返回首页 | 给 skynet 添加 mongo driver »

coroutine 的回收利用

这几天在 lua 和 luajit 的邮件列表上有人讨论 coroutine 的再利用问题。

前几天有个用 skynet 的同学给我写了封邮件,说他的 skynet 服务在产生了 6 万次 timeout 后,内存上升到了 50M 直到 gc 才下降。

这些让我重新考虑 skynet 的消息处理模块。skynet 对每条消息的相应都产生了一个新的 coroutine ,这样才能在消息处理流程中,可以方便的切换出去让调度器调度。诸如 RPC/ socket 读写这些 api 才能在用起来看成是同步调用,却在实现上不阻塞线程。

读源码可知,lua 的 coroutine 非常轻量(luajit 的略重)。但依旧有一些代价。频繁的动态生成 coroutine 对象也会对 gc 造成一定的负担。所以我今天花了一点时间优化了这个问题。

简单说,就是用自己写的 co_create 函数替换掉 coroutine.create 来构建 coroutine 。在原来的主函数上包裹一层。主函数运行完后,抛出一个 EXIT 消息表示主函数运行完毕。并把自己放到池中。如果池中有可利用的旧 coroutine ,则可以传入新的主函数重新利用之。

为了简化设计,如果 coroutine 中抛出异常,就废弃掉这个 coroutine 不再重复利用。为了防止 coroutine 池引用了死对象,需要在主函数运行完后,把主函数引用清空,等待替换。

具体实现参见这个 patch

ps. coroutine poll 故意没实现成弱表,而是在相应 debug GC 消息时再主动清空。

Comments

我也试过这种复用coroutine的做法,发现有两个问题:
1. 性能反而更差,我的实现稍有不同,但本质上是一样的,更差的原因估计是如何保留yield的变长参数,有兴趣可以看:https://gist.github.com/kingluo/64396435f7ed55af1b5b
2. 重用的coroutine的旧全局表会否有信息泄露的嫌疑?

@lpk

加上超时后会增加业务层特别多的复杂度,得不偿失.

如果必须要, 可以不用 call 而用 send 自己模拟. skynet 已经提供了足够的机制把超时做出来.

能否给coroutine加上超时限制呢,比如skynet.call,跨机器调用别一个服务,而被调用的服务没有响应的话,如果调用次数足够多,这会引起大量coroutine挂起.

Post a comment

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