了解云风的 skynet
开始应用《暗时间》里提到的理论,将 skynet 用自己的话来总结并写下来,这样能充分思考并转述为自己的记忆线索。
skynet 设计的理解
单个 skynet 节点
(1)愿景
充分利用多核。最初想法是多进程。像咱们 Node.js 里多核就只能是多进程了,因为每个 Node.js 进程是单线程的。
多进程是遵循 UNIX 设计哲学,工具链形式,分拆进程的形式来分拆模块,减少复杂度和耦合性,方便编程及维护。
后来云风他们发现 Lua 做为嵌入式脚本,写逻辑时很好用的,反正如何都要用 Lua,而且 Lua 提供了沙盒,这样多进程可以变为单进程多个沙盒,这样综合了多进程和单进程多线程的优势。多线程里共享资源,在同一进程地址空间,访问更高效。
(2)核心功能(门房?)
很精简,仅解决一个问题。
skynet 里不实现具体游戏逻辑,后者些放到一个一个动态库里(.so 文件)。skynet 将这些 .so 注册到自己里边,每个 .so 一个永不重复的 ID,类似于数据库的 AUTO_INCREMENT
。看描述这个 ID 是 skynet 自己运行时当次维护的,而不是模块配置好终身的 ID。模块的永久有效唯一标示为名字,skynet 提供了名字服务,可以给每个模块取一个易读的名字。
(3)核心不解决什么问题
skynet 主张所有服务在同一 OS 进程协作完成。核心里就没管跨机通讯,单个服务的崩溃和重启也没管,云风表示这些应该由上层处理,他有责任暴露错误,而不是隐藏。
这个设计的原因,游戏和操作系统不一样,操作系统默认不信任任何进程,各进程崩溃什么的不应影响其他进程,所以某个进程挂了,他就安葬它,而其他进程美好的生活。单游戏是为玩家服务的,某个环节出错都有可能造成玩家利益混乱,所以那里错了就整个流程(服务器)挂掉吧。没有必要让出错模块被隔离开,而其他模块却继续提供服务导出未预知行为。
上边说的东西应该上层考虑,使用 Lua 的沙盒就能做策略隔离。
(4)skynet 运行时逻辑流
skynet 负责且只负责将一个数据包从一个服务发送到同一进程的另一个服务里。发送服务直接调发送 API,skynet 收到数据包后,调用接受者服务的注册的 Callback,即发给了接受者服务。
skynet 保证在各模块初始化时、每个独立的 Callback 调用时,都是相互线程安全的。这样编写服务的人就不需要考虑多线程的任何问题了,只需专心处理给他的一个个数据包。
天龙八部的场景 Lua 有点像这里的单个服务。不知天龙的跨线程切场景情况在这里也可以给简化为单线程?(回头看源码再研究这个问题)
(5)消息调度
TODO
(6)gate 和 connection
TODO
skynet集群
集群里最多支持 255 个 skynet 节点,每个 skynet 节点有一个 ID,成为 harbor ID。这个 ID 是集群层面指定,可以人为分配,也可以由一个中央服务器协调分配。
(1)集群间通信
skynet 核心层纸负责在往外发消息时在 source 字段上加上自己的 harbor ID。而集群间的通信,是由单独的 harbor 服务来做的。skynet 将是往集群其他节点发的消息,就转发到 harbor 内。harbor 会跟集群内跟自己结识的 skynet 的 harbor 简历 TCP 链接。harbor 把消息发给目标 harbor。
harbor 间的通信为单向的 TCP 管道。
master 服务来同步全局的名字服务。每个 skynet 都会知道其他节点上装配了哪些服务,好路由过去。
(2)组播
TODO