skynet使用32bit的id(harbor id)来作为skynet上节点的唯一标识,其结构如下:
| 机器标识 | 服务节点id |
|---|---|
| 8bit | 24bit |
- 每个skynet进程与其它进程通讯的部件称为harbor
- 同一进程的“机器标识”是一致的,通过比较“机器标识”就可以判断是本机节点还是远程节点
- 机器标识一般的取值范围为
[1, 255](取0表示单进程) - 由于机器标识为8bit,所以集群中最多只可能有255个harbor
节点消息投递有以下2种方式:
- 根据harbor id直接投递
- 把消息转发给harbor服务
master/slave模式下节点的harbor之间通过master来建立网络,master既可以是一个单独的进程也可以依附于某个进程(默认)。
master其实就是一个简单的内存key-value数据库;key表示节点的名字/ID,value存储节点harbor的地址信息。
一个5节点网络示意图:
- master连接所有slave节点
- slave节点的新增/减少/服务变更都是先通知master,再由master转发
local harbor = require "skynet.harbor"
harbor.link(id)监控一个slave是否已断开(阻塞);如果harbor id对应的slave正常,这个api将阻塞;当slave断开时,立即返回。harbor.linkmaster()监控与master的连接是否正常。(这个api用于异常时的安全退出,当slave和master断开后,没有手段可以恢复)。harbor.connect(id)监控一个slave是否已连接(阻塞);如果harbor id对应的slave没有连接,这个api将阻塞直至它连上来。harbor.queryname(name)查询全局名字或本地名字对应的服务地址(阻塞);harbor.globalname(name, handle)注册一个全局名字;如果handle为空,则注册自己。
注意:harbor.link(id)和barbor.linkmaster()有一定开销,慎用!
参考源码:
lualib/skynet/harbor.luaservice/cslave.lua
- 当选项
harbor不为0时,意味着采用master/slave模式进行组网;此时必须配置选项:address,master等选项,启动slave服务;slave会根据master选项地址去连接master节点。 - 当选项
standalone不为NULL时,意味着启用master服务;master会监听standalone地址。
Title:master/slave组网示意图
slave3->master: “[H] slave_id slave_addr”
Note right of master: 确认本次握手是否有效
master->slave1: "[C] slave_id slave_addr"
master->slave2: "[C] slave_id slave_addr"
master-->slave3: "[W] 现有的slave数量"
Note right of master: 将slave3加入到slave组
slave1->slave3: 连接
slave3-->slave1: 接受连接
slave2->slave3: 连接
slave3-->slave2: 接受连接
参考源码:
service/cslave.luaservice/cmaster.lua
组网完成后,slave之间主要通过harbor服务来进行消息通讯;其流程如下:
Title:进程间harbor服务示意图
slave1的A服务-->slave1的harbor服务:进程内通讯
slave1的harbor服务-->slave2的harbor服务:跨进程通讯
slave2的harbor服务-->slave2的B服务:进程内通讯
harbor服务的功能包括以下:
- 主动/被动连接其他slave
- 注册新的服务名称
- 接管新到的套接字文件描述符
- ...
参考源码:
service/cslave.luaservice-src/service_harbor.c
- 过于强调CAP理论中的CA(一致性,可用性),对于P(分区容错性)的考虑较少,做出来的系统不够健壮(例如,master一旦挂了,整个服务gg)。
- 弹性不够,如果一个slave意外退出,这个harbor id就被废弃;如果这时一个新的进程以相同的harbor id接入,无法保证新旧地址不重复;需要自己解决harbor id复用问题。
master/slave机制建立在:节点之间是可靠连接,节点之间不会断开。的前提上,这个前提很多时候不一定成立。
-
设置master的配置文件和slave的配置文件,参考
examples/config,examples/config_log这两个文件 -
启动两个lua进程
# 启动master ./skynet examples/config # 启动slave ./skynet examples/config_log
cluster模式兼容master/slave模式,cluster模式其实就是用更上层的api来实现一个节点信息查询系统;通过查询注册的节点以及对应的服务信息来远程调用,从而实现消息路由。
cluster.call(node, address, ...)向一个节点上的一个服务提起一个请求,等待回应。cluster.send(node, address, ...)向一个节点上的一个服务推送一条消息。cluster.open(port)让当前节点监听一个端口。cluster.reload([config])重新加载当前节点的网络配置,通过config表来取代文件配置。cluster.proxy(node, address)为远程节点上的服务创建一个本地代理服务。cluster.snax(node, name, address)向远程节点上的snax服务创建一个本地代理。cluster.waitcluster.register(name, address)在当前节点上为一个服务起一个字符串名字,之后可以用这个名字取代地址。cluster.query(node, name)在远程节点上查询一个名字对应的地址。
- 编写cluster配置文件,配置集群内所有节点的名字和对应的监听端口;
- 将此配置文件部署到所有节点
参考示例:examples/clustername.lua
- 使用
cluster.reload来更新配置,lua table的配置优先级高于配置文件
参考示例:examples/clustername.lua
方法一:
-
编写配置文件,参考:
examples/config.c1,examples/config.c2这2个文件 -
启动一个节点,并注册服务信息
./skynet examples/config.c1
-
启动另一个节点
./skynet examples/config.c2
