文档章节

Skynet Cluster 简介

YLME
 YLME
发布于 2019/12/06 14:28
字数 1966
阅读 126
收藏 0

Skynet Cluster 简介

Cluster 模块负责 Skynet 节点之间的通信。

Cluster 概述

      | --- --- ---> |
node1 |              | node2
      | <-- --- ---  |

两个节点之间通信最多会创建 2 条 TCP 链路。如上图所示。

  • 节点 node1 主动向 node2 发起通信和 node2 的回应是一条 TCP 链接
  • 节点 node2 主动向 node1 发起通信和 node1 的回应是另一条 TCP 链接

Cluster 模块包含三个服务:clusterd clusteragentclustersender ,通信使用 gate 服务。

  • clusterd 服务提供管理功能,初始化 gate 服务监听网络连接。每个节点只存在一个 clusterd 服务。
  • gate 服务接收到新的 socket 连接时,通知 clusterd 服务,此时创建 clusteragent 服务接收数据。每个 clusteragent 服务对应一个 socket 连接。
  • 主动与其它节点通信时,clusterd 服务创建 clustersender 服务建立连接并发送数据。每个 clustersender 服务对应一个 socket 连接。

举个例子。假设存在如下节点配置:

center = "192.168.1.1:10000"
login1 = "192.168.1.2:10011"
login2 = "192.168.1.3:10012"
  • 若 login1 主动与 center 节点通信,则建立一条 TCP 连接,login1 创建一个 clustersender 服务与 center 创建的一个 clusteragent 服务对应。
  • 若 login2 主动与 center 节点通信,则建立一条 TCP 连接,login2 创建一个 clustersender 服务与 center 创建的另一个 clusteragent 服务对应。
  • 若 center 主动与 login1 节点通信,则建立一条 TCP 连接,center 创建一个 clustersender 服务与 login1 创建的一个 clusteragent 服务对应。
  • 若 center 主动与 login2 节点通信,则建立一条 TCP 连接,center 创建另一个 clustersender 服务与 login2 创建的一个 clusteragent 服务对应。

上述一共会创建 4 条 TCP 连接,4 个 clusteragent 服务和 4 个 clustersender 服务。

Cluster 接口

在使用 Skynet Cluster 之前,需要先设置配置信息,指定节点名称对应的 IP 地址及端口号。配置格式如下:db 是节点名称而 127.0.0.1:2528 是该节点对应的 IP 地址及端口号。

db = "127.0.0.1:2528"
db2 = "127.0.0.1:2529"

配置信息可通过配置文件给出,在 Skynet Config 中把文件名赋值给 cluster 即可。
此外也可以直接通过字符串给出配置信息。

调用 cluster.reload(config) 重载配置信息,config 可以是文件名或者字符串。

使用过程中通常涉及如下接口。接口位于库文件 lualib/skynet/cluster.lua 中。

调用 require "skynet.cluster" 创建 clusterd 服务。require 时调用 skynet.uniqueservice("clusterd") 创建唯一的 clusterd 服务。
调用 cluster.open(port) 监听网络连接。
调用 cluster.call(node, address, ...)node 节点发起请求。
调用 cluster.send(node, address, ...)node 节点推送数据。
调用 cluster.register(name, addr) 注册字符串 name 对应的地址。此时,请求方只需使用字符串 name 就可访问到对应的服务。 调用 cluster.query(node, name) 查询字符串 name 对应的服务地址。

通常,服务器调用 cluster.open 监听网络连接。客户端调用 cluster.call 或者 cluster.send 指定节点名称服务地址发起访问。

Cluster 加载配置流程

clusterd 服务在启动时会调用 loadconfig 函数加载配置,此外也可以通过 cluster.reload(config) 重载配置。

clusterd.lua:72 loadconfig 函数用于加载配置。配置格式就是前面提到的节点名称和对应的 IP 地址及端口号。加载完配置后,便可获取到节点对应的地址信息。具体流程如下。

  • 加载选项到 config 中。目前支持在配置中设置 __nowaiting = true ,若向节点发起连接请求时,未找到连接对应的地址,则直接报错而不是挂起协程等待该节点的地址信息被设置。
  • 加载配置数据到 node_address 中。
  • ct.namequerynot config.nowaitingtrue ,则唤醒等待节点地址信息的协程。
local ct = connecting[name]
if ct and ct.namequery and not config.nowaiting then
    skynet.error(string.format("Cluster node [%s] resloved : %s", name, address))
    skynet.wakeup(ct.namequery)
end
  • config.nowaitingtrue 则表示不等待节点的配置信息,此时唤醒所有的协程。
if config.nowaiting then
    -- wakeup all connecting request
    for name, ct in pairs(connecting) do
        if ct.namequery then
            skynet.wakeup(ct.namequery)
        end
    end
end
  • 若节点的地址发生变更,则向新地址重新发起连接请求。
for _, name in ipairs(reload) do
    -- open_channel would block
    skynet.fork(open_channel, node_channel, name)
end

Cluster 监听网络连接

cluster.open 函数用于监听网络连接。clusterd.lua124 command.listen 处理具体的监听逻辑,网络模块采用 gate 服务。处理来自 gate 服务的消息来处理网络请求。

function command.listen(source, addr, port)
    local gate = skynet.newservice("gate")
    if port == nil then
        local address = assert(node_address[addr], addr .. " is down")
        addr, port = string.match(address, "([^:]+):(.*)$")
    end
    skynet.call(gate, "lua", "open", { address = addr, port = port })
    skynet.ret(skynet.pack(nil))
end

Cluster 发起网络连接请求

调用 cluster.sendcluster.call 向节点发送数据,若还未建立连接,则会先尝试建立连接。概要流程如下:

  • cluster.lua:8 request_sender 函数中调用 pcall(skynet.call, clusterd, "lua", "sender", node) 通过 clusterd 服务创建 clustersender 服务,创建完服务后,则连接建立完成。
  • clustersender 服务发送数据,即可向目标节点发送数据。

<u>获取 clustersender 服务</u>

对于 cluster.call 函数,通过 cluster.lua41 get_sender 函数获取 clustersender 服务,若处于创建过程中,则调用 skynet.wait(task) 等待。
对于 cluster.send 函数,由于不能阻塞调用,所以通过 get_queue 函数获取 clustersender 服务。若处于创建 clustersender 服务的过程中,则把请求插入到队列中然后返回:table.insert(task_queue[node], table.pack(address, ...)) 。通过对 task_queue 设置元表 setmetatable(task_queue, { __index = get_queue } ) ,来触发首次调用 request_sender 函数。

函数 request_sender 的注意点在于需要按顺序逐个唤醒等待的协程。由于在 ipairs(q) 的过程中,可能存在对 q 的修改是 table.insert 操作,因此是允许的。最后设置 sender[node] = c 表示服务创建完毕,后续通过 sender 获取即可。

for _, task in ipairs(q) do
    if type(task) == "table" then
        if c then
            skynet.send(c, "lua", "push", task[1], skynet.pack(table.unpack(task,2,task.n)))
        end
    else
        skynet.wakeup(task)
        skynet.wait(confirm)
    end
end
task_queue[node] = nil
sender[node] = c

<u>创建 clustersender 服务,建立网络连接</u>

函数 clusterd.lua:14 open_channel 完成连接建立过程。流程如下:

  • 若当前正处于连接建立的过程中,则挂起当前协程,等待。
local ct = connecting[key]
if ct then
    local co = coroutine.running()
    table.insert(ct, co)
    skynet.wait(co)
    return assert(ct.channel)
end
  • 获取节点的 IP 地址数据:local address = node_address[key] 。若 not config.nowaitingtrue 则需要等待节点的地址数据,此时也会被挂起,等待在 loadconfig 中被唤醒。
  • 创建 clustersender 服务,调用 pcall(skynet.call, c, "lua", "changenode", host, port)clustersender 发送 changenode 消息,由 clustersender 服务 connect 目标节点。
  • 完成连接建立,唤起 connecting 中之前被挂起的连接请求。
connecting[key] = nil
for _, co in ipairs(ct) do
    skynet.wakeup(co)
end
  • 检查 node_address[key] 节点地址是否发生变更,若变更则再次调用 open_channel 函数。
if node_address[key] ~= address then
    return open_channel(t,key)
end

于是,连接建立完成,可与目标节点通信。

Cluster 接收网络连接数据

clusterd 服务创建 gate 服务,接收来自 gate 服务的消息处理网络请求。当有新的网络连接时 clusterd.lua195 command.socket 函数处理新的连接,此时 subcmd == "open" 创建 clusteragent 服务接收网络数据。
subcmd == "closer" or subcmd == "error" 表示网络连接已经断开,此时处理连接断开逻辑。

function command.socket(source, subcmd, fd, msg)
    if subcmd == "open" then
        skynet.error(string.format("socket accept from %s", msg))
        -- new cluster agent
        cluster_agent[fd] = false
        local agent = skynet.newservice("clusteragent", skynet.self(), source, fd)
        local closed = cluster_agent[fd]
        cluster_agent[fd] = agent
        if closed then
            skynet.send(agent, "lua", "exit")
            cluster_agent[fd] = nil
        end
    else
        if subcmd == "close" or subcmd == "error" then
            -- close cluster agent
            local agent = cluster_agent[fd]
            if type(agent) == "boolean" then
                cluster_agent[fd] = true
            elseif agent then
                skynet.send(agent, "lua", "exit")
                cluster_agent[fd] = nil
            end
        else
            skynet.error(string.format("socket %s %d %s", subcmd, fd, msg or ""))
        end
    end
end

Cluster 具体的发送和接收数据逻辑

发送数据的细节见 clustersender 服务,接收数据的细节见 clusteragent 服务。

© 著作权归作者所有

YLME
粉丝 17
博文 48
码字总数 53072
作品 0
广州
程序员
私信 提问
游戏服务器架构调研报告

服务器架构调研报告 刘源霖20151119 1. 前言 本文档主要是调研分析新的手游服务端架构,为下一款手游服务端研发提供可参考的方案。主要的参考点是数据持久化,并发效率,分布式,沙盒机制,热...

shezjl
2016/01/22
2.5K
1
Skynet 1.2.0 发布,轻量级在线游戏框架

今天 Skynet 的作者云风在其博客宣布推出 skynet 1.2.0。Skynet 是一个轻量级的在线游戏框架,它也可以用于许多其他领域。 下载地址:https://github.com/cloudwu/skynet/releases/tag/v1.2...

局长
2018/11/06
2.5K
3
为skynet移植一个lua-websocke库

为skynet移植一个lua-websocke库 简介 目前大部分游戏、移动互联网、H5客户端主要由JavaScript、Lua、C#、C++等语言进行逻辑开发, 其主要通讯方案便是基于HTTP协议的接口请求与Websocket的推...

水果糖的小铺子
2019/06/22
41
0
开源并发框架 Skynet 发布第一个正式版 v0.1.0

距离 skynet 开源项目的公布 已经有 20 月+ 了,如果从闭源阶段算起,已经超过了 30 个月。在我们公司内部有五个项目使用 skynet 开发,据有限的了解,在我们公司之外,至少有两个正式项目使...

C_Z
2014/04/23
4.1K
6
skynet 入门笔记(1):Hello, skynet!

Hello, Skynet! Skynet这名字让我想起了经典科幻电影《终结者》里毁灭人类世界的终极人工智障,skynet的官方文档是挺给力的,但是没有那么好的引导机制,看了半天的文档还是不知道该怎么用s...

uniqptr
2018/06/27
328
0

没有更多内容

加载失败,请刷新页面

加载更多

Kettle自定义jar包供javascript使用

我们都知道 Kettle 是用 Java 语言开发,并且可以在 JavaScript 里面直接调用 java 类方法。所以有些时候,我们可以自定义一些方法,来供 JavaScript 使用。 本篇文章有参考自:https://www...

CREATE_17
昨天
102
0
处理CSV文件中的逗号

我正在寻找有关如何处理正在创建的csv文件的建议,然后由我们的客户上传,并且该值可能带有逗号(例如公司名称)。 我们正在研究的一些想法是:带引号的标识符(值“,”值“,”等)或使用|...

javail
昨天
79
0
如何克隆一个Date对象?

将Date变量分配给另一个变量会将引用复制到同一实例。 这意味着更改一个将更改另一个。 如何实际克隆或复制Date实例? #1楼 简化版: Date.prototype.clone = function () { return new ...

技术盛宴
昨天
73
0
计算一个数的数位之和

计算一个数的数位之和 例如:128 :1+2+8 = 11 public int numSum(int num) { int sum = 0; do { sum += num % 10; } while ((num = num / 10) > 0); return sum;......

SongAlone
昨天
124
0
为什么图片反复压缩后普遍会变绿,而不是其他颜色?

作者:Lion Yang 链接:https://www.zhihu.com/question/29355920/answer/119088684 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 业余版概要:安卓的...

shzwork
昨天
81
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部