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