Skynet是什么

Skynet是什么呢?

我们希望游戏服务器能够充分利用多核优势,将不同的业务放在独立的执行环境中处理,协同工作。这个执行环境最早期望是利用操作系统的进程,后来发现如果必定采用嵌入式语言如Lua,那么独立的操作系统进程的意义不大。LuaState已经提供了良好的沙盒,隔离不同执行环境,而多线程模式可以使得状态共享、数据交换更加高效。但是多线程模型又存在诸多弊病,比如复杂的线程锁、线程调度问题等。这些问题都可以通过减小底层的规模精简设计,最终把危害限制在很小的范围内。

早期的Skynet v0版本中,Skynet是使用Erlang+C driver开发的,Erlang中的多进程似乎是:每个玩家都有一个代理服务。当在在40v40时,使用台式机测试时是存在困难的,于是Cloud滋生了重新考虑之前的设计的念头。由于之前的设计方案是:每个代理的状态都需要通知给其他代理,这造成了大量内部消息重复,这明显是可以优化的。

掂量下,如果设计一个共享无锁队列,避免了无谓复制,内部消息包可以降低90%,整体性能能提升15%左右。为了验证Skynet v0的性能,编写了一个简单的Skynet v1,v1完全使用C作底层通信框架,摒弃了之前ZeroMQ提供通信的部分,提供Lua接口编写应用服务,整体代理在4000多行的C代码和1000多行的Lua代码,完成了Skynet v0一样的功能。

  • Skynet提供了一个简洁、稳定、高效、高可用的分布式服务开发框架。
  • Skynet是一个轻量级通用的服务器基础框架
  • Skynet是基于C与Lua的开源服务端并发框架,使用单进程多线程Actor模型。
  • Skynet服务器支持10K+客户端接入和处理

Skynet当前规模是8K多行的C代码和2K多行Lua代码,实现了一个多线程高并发的在线游戏后台服务框架,提供定时器、开发调度、服务扩展框架、异步消息队列、命名服务等基础能力,支持Lua脚本。

Skynet是一个轻量级网络服务器架构而非完整的游戏服务端,它是服务端的最底层框架,和游戏业务相关的服务都是基于此框架之上开发的。其功能只是管理好服务(加载和调度)和服务之间的调用(请求和响应)。

Skynet特点的什么

  • 少量C代码和大量Lua代码组成
  • 基于Actor模型,天然多线程。
  • 天然集成网络、数据库访问功能
  • 使用Lua的协程处理消息永不堵塞
  • 自带集群功能
  • 官方只支持Linux

Skynet的优点是什么

  • 高低级语言配合

Skynet是融合了低级语言C消息框架和高级动态语言Lua的混合体,这种结构称为hybird framework混合框架,使用运行高效的C来编写服务节点,使用Lua开发高效且安全隔离的上层业务。

  • 组件化能力

Skynet内核(C部分)自身支持加载模块*.so,可使用C语言编写性能有要求的服务节点,通过消息与其他节点配合。而Lua又是对C语言极为友好的动态语言,所以可找到很多Lua的C扩展。

Skynet核心是什么

  • C实现的消息循环和组件加载机制
  • Lua实现的以消息为中心的进入退出协程的包装层

Skynet核心解决什么问题呢?

作为核心功能,Skynet仅解决一个问题:把符合规范的C模块从动态库(.so文件)中启动起来,绑定一个永不重复(即使模块退出)的数字ID作为其处理器handle。这里我们称模块为服务service,服务之间可以自由发送消息,每个模块可以向Skynet框架注册一个回调函数callback,用来接收发给它的消息。每个服务都是一个个消息包驱动,当没有包到来时,它们就会处于挂起状态,对CPU资源零消耗。如果需要自主逻辑则可利用Skynet提供的timeout消息定期触发。

Skynet提供了名字服务,可以给特定的服务起一个易读的名字,而不是使用ID来指代。因为ID和运行时状态相关,无法保证每次启动服务都有一致的ID,当名字却可以。

Skynet核心不解决什么问题?

Skynet的消息传递都是单向的,以数据包为单位传递。并没有定义类似于TCP连接的概念,也没有约定RPC调用的协议。不规定数据包的编码方式,也没有提供一致的复杂数据结构的列集API。

Skynet原则上主张所有服务器都在同一个操作系统进程上协作完成,所以在核心层内部考虑跨机器通讯机制。Skynet不为单独服务的崩溃、重启提供支持,和普通的单线程程序一样,你要为你代码中的bug和意外负责。和操作系统不同的是:操作系统会认为用户进程都是不可靠的,它不会让一个用户进程的错误影响到另一个进程。但Skynet内所有的服务都有统一的目的,为游戏的最终客户服务。因此某个环节出了错误都可能是致命的,因此没有必要被问题隔离开。

简单来说,Skynet只负责把一个数据包从一个服务内发送出去,让同一进程内的另一个服务接收然后调用对应的callback函数处理。Skynet保证模块的初始化过程时,每个独立的callback都是相互线程安全的。编写服务的人不需要特别的为多线程环境考虑任何问题,专心处理发送给它的一个个数据包即可,其实这就是Erlang的Actor模型。

为了提供高效的服务间通讯,Skynet并不关心数据包是怎样被打包的,它甚至不要求这个数据包内的数据是连续的,虽然这样做很危险,在跨机通讯中除非你保证你所有的数据包绝对不被传递到当前所在进程中。因为它仅仅是把数据包的指针以及声明的数据包长度传递出去。由于服务都是在同一个进程内,因此接收方取得这个指针后就可以直接处理其引用的数据了。这个机制在必要时,可以保证绝对的零拷贝,几乎等于在同一线程内做一次函数调用的开销,当然这只是Skynet提供性能上可能性。推荐一种更为可靠但性能略低的解决方案:约定每个服务发送出来的包都复制到用malloc分配出来的连续内存。接收方在处理完这个数据块,也就是在处理的callback函数调用完毕时,会默认调用free函数释放掉所占用的内存空间。简单来说就是:发送方申请内存而接收方释放内存。

你可能感兴趣的:(Skynet是什么)