skynet 分布式,skynet性能

  skynet 分布式,skynet性能

  服务器高层架构-冯云天网

  用天网之手撕碎一个万人网游。

  天网是我们游戏服务器的底层框架。我在选技术的时候仔细看了它的源代码,发现它是C语言的工程模型。大部分游戏服务器要么用C,要么用java,用C的非常少,但是天网通过C和Lua的结合,实现了一个高效的游戏框架。C层没有冗余的三方库,而是紧凑的核心结构,提供了核心的消息处理框架。Lua层用来写游戏逻辑,降低了开发门槛。

  目前天网广泛应用于阿里游戏。据我所知,热门三国志用的是天网,我们的游戏当然也用这个框架,已经稳定运行一年多了。

  说到天网,不能算是游戏服务器框架。它只是为游戏服务器提供一些必要的基础设施,可以用来设计符合要求的上层逻辑。根据冯云的说法,天网实现了类似于Erlang的Actor模型,它本质上是一个高度并发的消息处理框架。消息从底层分发到上层“服务”进行处理。这里的服务可以用C写,当然大部分时候是用Lua写的,每个Lua服务都是一个独立的Lua虚拟机,保证了服务之间的环境隔离。Lua服务使用协同流程来处理消息。当它需要与其他服务通信时,协同过程可以暂停,直到其他服务返回,然后继续。这一方面让我们可以像写同步代码一样“顺序执行”,当协同学进程暂停时,服务可以处理其他消息,保证了消息的高并发。

  由于天网内核的精简,很多人抱着开箱即用的想法。后来发现门槛其实不低。还是需要你熟悉游戏服务器业务,知道自己想要达到的目标,然后自己去做。但正是因为它的简化,才具有高度的可定制性。

  天网的核心功能如果要用一句话来形容天网的核心功能是什么:依然是基于事件的高并发消息处理框架。事件主要来自网络、定时器、信号通知等。当事件被触发时,天网将这些事件编码成消息结构,发送给感兴趣的服务进行处理;当一个服务处理一个消息时,它也可以主动向其他服务发送一个消息。所以,他是事件驱动的,没有前述事件,天网什么都做不了。

  天网的核心数据结构是skynet_context。我对Erlang不熟悉,分不清它对应的是什么结构。但它实际上也像操作系统中进程的概念。在这里我们称之为服务。服务包含以下内容:

  服务句柄:类似于进程ID,用于唯一标识服务。

  服务:模块以动态库的形式提供。创建skynet_context时,必须指定模块的名称。天网加载模块并创建模块的一个实例。实例向服务注册一个回调函数来处理服务的消息。

  消息队列:每个服务都有一个消息队列。当队列中有消息时,它将被主动链接到全局链表。天网启动一定数量的工作线程,不断从全局链表中取出消息队列,将消息发送给服务的回调函数进行处理。

  下面的结构图显示了天网的核心结构:

  Handles每个服务都与一个句柄相关联,这个句柄在Skynet _ handle.h C中实现,句柄是一个32位无符号整数,最高的8位代表集群ID(已弃用),剩余的24位是服务ID。

  Handle_storage用于存储ID和skynet_context的映射:

  //句柄存储结构

  结构句柄_存储{

  struct rwlock锁;//读写锁

  uint32_t港;//群集ID

  uint32 _ t handle _ index//当前句柄索引

  int slot _ size//插槽数组大小

  struct skynet_context **槽;//skynet_context数组

  … …

  };

  服务模块让我们先来看看创建服务的API:

  //创建服务:name是服务模块的名称,parm是参数,模块自己解释含义。

  struct skynet _ context * skynet _ context _ new(const char * name,const char * parm);

  这里的name参数是模块名。天网根据这个名字加载模块,调用约定的导出函数。这个过程大概是这样的:

  获得模块后,调用skynet_module_instance_create函数创建模块实例。

  然后调用skynet_module_instance_init来初始化实例。通常情况下,实例在初始化时调用skynet_callback向skynet设置一个回调函数,以后的消息处理将由这个回调函数处理。

  当消息队列创建服务时,也会创建一个新的消息队列。消息队列在skynet_mq.ch中实现,它由以下结构表示:

  //消息队列

  结构消息队列{

  struct spinlock锁;

  uint32_t句柄;//关联的服务句柄

  int cap//队列容量

  int head//队列头的位置

  int tail//队列末尾的位置

  结构skynet _ message * queue//消息结构的数组

  struct message _ queue * next//指向下一个消息队列

  … …

  };

  Next指向下一个消息队列,也就是说message_queue会形成一个链表,然后由global_queue保存,是这样的:

  结构全局队列{

  struct message _ queue * head

  struct message _ queue * tail

  struct spinlock锁;

  };

  global_queue持有的链表就是需要处理消息的消息队列。这个过程如下:

  调用skynet_mq_push将消息推入消息队列。

  然后调用skynet_globalmq_push将消息队列链接到global_queue的末尾。

  从全局链表中弹出一个消息队列来处理队列中的消息。如果队列中的消息被处理,它们将不会被推回到全局链表中。如果不处理,就推回全局链表,等待下一次处理。

  描述比较简单,具体细节还是要查函数Skynet _ context _ message _ dispatch。

  天网启动和信息处理。在介绍了该服务的三个重要组件之后,您现在可以看看skynet_context的内容:

  结构skynet_context {

  void *实例;//服务模块的实例指针

  struct skynet _ module * mod//服务模块指针

  void * cb _ ud//回调函数的用户数据

  天网_ cb cb//业务处理消息的回调函数

  struct message _ queue * queue//消息队列

  uint32_t句柄;//服务句柄

  … …

  };

  其实最核心的部分就是上面说的三个,那么天网是如何启动并不断处理消息的呢?答案是天网_start这个函数:

  第一步是初始化各个功能模块,比如句柄、消息队列、模块、定时器、套接字等等。

  然后创建一个日志服务。创建引导服务。

  然后创建一定数量的工作线程,可以通过配置指定,工作线程的职责是分发消息。

  创建定时器线程记录时间,实现超时事件;

  创建一个sokcet线程来处理sokcet消息。套接字和超时事件最终将被转换为消息,这些消息将被发送到工作线程进行服务处理。

  创建一个监控线程,用来监控服务中是否存在无限循环。

  如前所述,天网由事件驱动。这里主要有两个事件,一个是socket,一个是timeout。分别由两个线程运行。

  工作线程的核心逻辑是调用Skynet _ context _ message _ dispatch来调度消息。分派之后,它会进入睡眠状态,等待另外两个线程醒来。这是非常典型的生产消费模式。这是大多数服务器程序的核心功能,天网也不例外:

  勿忘你的倡议心灵雅原创作品,博主,

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • office2010激活密钥大全 怎么永久激活office2010
  • project2010产品密钥免费_project2010激活密钥永久激活码
  • c语言调用退出函数 c语言退出整个程序怎么写
  • c语言中怎么给函数初始化 c语言的初始化语句
  • c语言编写函数计算平均值 c语言求平均函数
  • chatgpt是什么?为什么这么火?
  • ChatGPT为什么注册不了?OpenAI ChatGPT的账号哪里可以注册?
  • OpenAI ChatGPT怎么注册账号?ChatGPT账号注册教程
  • chatgpt什么意思,什么是ChatGPT ?
  • CAD中怎么复制图形标注尺寸不变,CAD中怎么复制图形线性不变
  • cad中怎么创建并使用脚本文件,cad怎么运行脚本
  • cad中快速计算器的功能,cad怎么快速计算
  • cad中快速修改单位的方法有哪些,cad中快速修改单位的方法是
  • cad中心点画椭圆怎么做,cad轴测图怎么画椭圆
  • CAD中常用的快捷键,cad各种快捷键的用法
  • 留言与评论(共有 条评论)
       
    验证码: