0%

skynet基础

Skynet 基础笔记

方便自己查询

Config

启动 skynet 服务器需要提供一个配置文件,配置文件的编写可以参考 examples/config ,下面是一个简单的配置文件范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
root = "./"
thread = 8
logger = nil
harbor = 1
address = "127.0.0.1:2526"
master = "127.0.0.1:2013"
start = "main" -- main script
bootstrap = "snlua bootstrap" -- The service for bootstrap
standalone = "0.0.0.0:2013"
luaservice = root.."service/?.lua;"..root.."test/?.lua;"..root.."examples/?.lua"
lualoader = "lualib/loader.lua"
snax = root.."examples/?.lua;"..root.."test/?.lua"
cpath = root.."cservice/?.so"

这个配置文件实际上就是一段 lua 代码,通常,我们以 key = value 的形式对配置项赋值。skynet 在启动时,会读取里面必要的配置项,并将暂时用不到的配置项以字符串形式保存在 skynet 内部的 env 表中。这些配置项可以通过 skynet.getenv 获取。
必要的配置项有:

  • thread 启动多少个工作线程。通常不要将它配置超过你实际拥有的 CPU 核心数。
  • bootstrap skynet 启动的第一个服务以及其启动参数。默认配置为 snlua bootstrap ,即启动一个名为 bootstrap 的 lua 服务。通常指的是 service/bootstrap.lua 这段代码。
  • cpath 用 C 编写的服务模块的位置,通常指 cservice 下那些 .so 文件。如果你的系统的动态库不是以 .so 为后缀,需要做相应的修改。这个路径可以配置多项,以 ; 分割。

DataCenter

datacenter 类似一个全网络共享的注册表。它是一个树结构,任何人都可以向其中写入一些合法的 lua 数据,其它服务可以从中取出来。所以你可以把一些需要跨节点访问的服务,自己把其地址记在 datacenter 中,需要的人可以读出。
datacenter 是一个 lua 库,使用:

1
2
3
4
5
local datacenter = require "datacenter"

datacenter.set(key1, key2, ... , value) -- 可以向 key1.key2 设置一个值 value 。这个 api 至少需要两个参数,没有特别限制树结构的层级数。
datacenter.get(key1, key2, ...) -- 从 key1.key2 读一个值。这个 api 至少需要一个参数,如果传入多个参数,则用来读出树的一个分支。
datacenter.wait(key1, key2, ...) -- 同 get 方法,但如果读取的分支为 nil 时,这个函数会阻塞,直到有人更新这个分支才返回。当读写次序不确定,但你需要读到其它地方写入的数据后再做后续事情时,用它比循环尝试读取要好的多。wait 必须作用于一个叶节点,不能等待一个分支。

UniqueService

我们可以通过 skynet.newservice 启动一个 lua 编写的服务。同一段脚本可以启动多份,每个有不同的地址。地址是区分不同服务的唯一标识。
但有时,整个系统中解决一类事务只需要一个服务,在系统启动时,它便启动好,而其它服务需要知道它的地址以便于使用它。这个时候,使用 skynet.uniqueservice 是更好的选择。
skynet.uniqueservice 和 skynet.newservice 的输入参数相同,都可以以一个脚本名称找到一段 lua 脚本并启动它,返回这个服务的地址。但和 newservice 不同,每个名字的脚本在同一个 skynet 节点只会启动一次。如果已有同名服务启动或启动中,后调用的人获得的是前一次启动的服务的地址。
很多skynet 库都附带有一个独立服务,你可以在库的初始化时,写上类似的语句:

1
2
3
4
local SERVICE
skynet.init(function()
SERVICE = skynet.uniqueservice "foobar"
end)

这个范例会注册一个初始化函数去初始化 SERVICE 变量。而你的库函数就可以使用 SERVICE 这个地址来访问对应的唯一的 foobar 服务了。
和 DataCenter 不同,uniqueservice 是一个专用于服务管理的模块。它在服务地址管理上做了特别的优化。因为对于同一个名字,只允许启动一次,且不准更换。所以,在实现上,我们可以在每个节点缓存查询过的结果,而不必每次都去中心节点查询。

LuaAPI

skynet 提供了一个叫做 skynet 的 lua 模块提供给 snlua 服务承载的 Lua 脚本使用。你只需要编写一个后缀为 .lua 的脚本文件,把文件名作为启动参数,启动 snlua 即可。(关于脚本路径的配置,见 Config)
通常,你需要在脚本的第一行写上:

1
local skynet = require "skynet"

注:skynet 这个模块不能在 skynet 框架之外使用,所以你用标准的 lua 解析器运行包含了 skynet 模块的代码会立即出错。这是因为,每个 skynet 服务都依赖一个 skynet_context 的 C 对象,它是由 snlua 导入到 lua 虚拟机中的。
每个 skynet 服务,最重要的职责就是处理别的服务发送过来的消息,以及向别的服务发送消息。每条 skynet 消息由五个元素构成。

  1. session :大部分消息工作在请求回应模式下。即,一个服务向另一个服务发起一个请求,而后收到请求的服务在处理完请求消息后,回复一条消息。session 是由发起请求的服务生成的,对它自己唯一的消息标识。回应方在回应时,将 session 带回。这样发送方才能识别出哪条消息是针对哪条的回应。session 是一个非负整数,当一条消息不需要回应时,按惯例,使用 0 这个特殊的 session 号。session 由 skynet 框架生成管理,通常不需要使用者关心。
  2. source :消息源。每个服务都由一个 32bit 整数标识。这个整数可以看成是服务在 skynet 系统中的地址。即使在服务退出后,新启动的服务通常也不会使用已用过的地址(除非发生回绕,但一般间隔时间非常长)。每条收到的消息都携带有 source ,方便在回应的时候可以指定地址。但地址的管理通常由框架完成,用户不用关心。
  3. type :消息类别。每个服务可以接收 256 种不同类别的消息。每种类别可以有不同的消息编码格式。有十几种类别是框架保留的,通常也不建议用户定义新的消息类别。因为用户完全可以利用已有的类别,而用具体的消息内容来区分每条具体的含义。框架把这些 type 映射为字符串便于记忆。最常用的消息类别名为 “lua” 广泛用于用 lua 编写的 skynet 服务间的通讯。
  4. messsage :消息的 C 指针,在 Lua 层看来是一个 lightuserdata 。框架会隐藏这个细节,最终用户处理的是经过解码过的 lua 对象。只有极少情况,你才需要在 lua 层直接操作这个指针。
  5. size :消息的长度。通常和 message 一起结合起来使用。