skynet集群 --- master/slave 模式

  skynet本身解决的核心问题是充分利用同一台机器的多核的处理能力。云风在描述集群时,强调说skynet只提供了构建集群的组件。那是因为不是所有项目遇到的问题都能够用统一的解决方案的。还提出任何企图抹平服务运行位置差异的设计都需要慎重考虑,很可能存在设计问题,因为集群协作不与单机多服务工作,集群中可能对方的服务并未启动,而单机工作中,可以认为不会只有一部分出错(即不会说当前功能正在运行,但是本机目标服务未启动的情况)。

  skynet自带两种集群基础架构,我们接下来要拜读的是master/slave 方式。

  

复制代码
-- examples/config

...
harbor = 1
address = "127.0.0.1:2526"
master = "127.0.0.1:2013"
standalone = "0.0.0.0:2013"
...
复制代码

 

  如上代码段所示是skynet例子中的config配置表,如需要使用master/slave的集群模式,只需要配置上面4个字段即可。

  集群中 harbor 字段,表示集群机子的id,由8位整数表示,其中0代表不启用master/slave 集群模式,所以一个集群中,只能有255个slave节点。在一个skynet启动的服务地址中,服务地址可以用32位的整数表示,其中高8位即代表harbor值,剩下的24位即是单机启动的service占用。

  address 则表示节点slave监听的ip与端口。

  master 则表示用来维护slave节点间的关系的服务。

  standalone 表示当前机子会启动master服务。

  master/slave 是用来处理单机器运算能力不足的补充,所以忽略了集群间故障的处理机制,即当一台机器出错未启动或者崩了的情况下,整个集群会出现问题,即master/slave 是默认整个集群都是正常运作的。

  接下来追踪整个master/slave 是怎么启动的,并且多个slave是怎么交互的,master主要起到什么功能,service_harbor服务又是怎么工作的,service_harbor在实现时可能出现什么问题?

  master/slave 的启动

  master/salve 的启动是由bootstrap服务执行的,首先先判断harbor是否大于0,大于0则表示启用master/slave 集群模式,然后先判断standalone来确定当前机器是否需要启动master服务,最后启动slave节点。

  master的启动如下,会监听master配置的ip与端口。

复制代码
 1 -- cmaster.lua
 2 skynet.start(function()
 3     local master_addr = skynet.getenv "standalone"
 4     skynet.error("master listen socket " .. tostring(master_addr))
 5     local fd = socket.listen(master_addr)
 6     socket.start(fd , function(id, addr)
 7         skynet.error("connect from " .. addr .. " " .. id)
 8         socket.start(id)
 9         local ok, slave, slave_addr = pcall(handshake, id)
10         if ok then
11             skynet.fork(monitor_slave, slave, slave_addr)
12         else
13             skynet.error(string.format("disconnect fd = %d, error = %s", id, slave))
14             socket.close(id)
15         end
16     end)
17 end)
View Code
复制代码

  master启动完成后,会启动当前机子的slave节点,slave启动过程如下。

  首先slave会listen一个端口,然后会连接master服务建立连接,并且启动service_harbor服务,之后slave会发送 'H harbor_id harbor_address" 消息给master服务,master服务执行 handshake 函数(此函数通知已有slave节点们去分别跟新的slave节点进行连接并且返回 “W n" 已有节点数量给新的slave节点)最后master会启动一个协程来监听该slave节点的通讯(主要是处理该新节点所需要创建的全局服务名,以及该节点对全局服务名的一些操作,例如查询),salve等待master的"W n"消息获得已有节点数量,之后slave节点会启动一个协程来处理与master的通讯(主要是监听新slave节点的连接,全局服务名的注册,断开连接等操作),最后会等待n个已有节点来连接当前slave节点,连接完成后,slave服务执行在启动期间创建的任务队列(主要是有其他新节点上来,slave启动完成后需要去连接新节点),并且将获取到的global_name 转发给service_harbor 服务。

  至此,slave节点启动完毕。

  

 

 

 

复制代码
 1 -- cslave.lua
 2 
 3 skynet.start(function()
 4     local master_addr = skynet.getenv "master"
 5     local harbor_id = tonumber(skynet.getenv "harbor")
 6     local slave_address = assert(skynet.getenv "address")
 7     local slave_fd = socket.listen(slave_address)
 8     skynet.error("slave connect to master " .. tostring(master_addr))
 9     local master_fd = assert(socket.open(master_addr), "Can't connect to master")
10 
11     skynet.dispatch("lua", function (_,_,command,...)
12         local f = assert(harbor[command])
13         f(master_fd, ...)
14     end)
15     skynet.dispatch("text", monitor_harbor(master_fd))
16 
17     harbor_service = assert(skynet.launch("harbor", harbor_id, skynet.self()))
18 
19     local hs_message = pack_package("H", harbor_id, slave_address)
20     socket.write(master_fd, hs_message)
21     local t, n = read_package(master_fd)
22     assert(t == "W" and type(n) == "number", "slave shakehand failed")
23     skynet.error(string.format("Waiting for %d harbors", n))
24     skynet.fork(monitor_master, master_fd)
25     if n > 0 then
26         local co = coroutine.running()
27         socket.start(slave_fd, function(fd, addr)
28             skynet.error(string.format("New connection (fd = %d, %s)",fd, addr))
29             socketdriver.nodelay(fd)
30             if pcall(accept_slave,fd) then
31                 local s = 0
32                 for k,v in pairs(slaves) do
33                     s = s + 1
34                 end
35                 if s >= n then
36                     skynet.wakeup(co)
37                 end
38             end
39         end)
40         skynet.wait()
41         socket.close(slave_fd)
42     else
43         -- slave_fd does not start, so use close_fd.
44         socket.close_fd(slave_fd)
45     end
46     skynet.error("Shakehand ready")
47     skynet.fork(ready)
48 end)
View Code
复制代码

  在启动slave时,会启动 service_harbor 的 c 服务。

   service_harbor 服务主要处理发送给一个服务的分发,判断是否需要走集群发送。

  当业务逻辑需要发送消息时,会先判断是发送到本机service还是到集群的service,如果是本机service,直接将消息插入到目标service的次级消息队列中,如果是集群的service,需要会先将消息插入到本机service_harbor 的服务当中,然后在service_harbor服务当中进行分发。

  这里有一点需要注意的是,如果目标service是在刚跟本机slave进行握手,即当前在service_harbor中的slave状态是 STATUS_HANDSHAKE 时,会先将消息插入到目标slave的队列当中,等到目标slave完成与本机slave的socket建立后,然后对目标slave的队列的消息一一处理。

复制代码
 1 // skynet_server.c
 2 int
 3 skynet_send(struct skynet_context * context, uint32_t source, uint32_t destination , int type, int session, void * data, size_t sz) {
 4     ...
 5     if (skynet_harbor_message_isremote(destination)) {
 6         struct remote_message * rmsg = skynet_malloc(sizeof(*rmsg));
 7         rmsg->destination.handle = destination;
 8         rmsg->message = data;
 9         rmsg->sz = sz & MESSAGE_TYPE_MASK;
10         rmsg->type = sz >> MESSAGE_TYPE_SHIFT;
11         skynet_harbor_send(rmsg, source, session);
12     } else {
13         struct skynet_message smsg;
14         smsg.source = source;
15         smsg.session = session;
16         smsg.data = data;
17         smsg.sz = sz;
18 
19         if (skynet_context_push(destination, &smsg)) {
20             skynet_free(data);
21             return -1;
22         }
23     }
24     return session;
25 }
View Code
复制代码

  以上即是基本的master/slave集群方式的启动并且通信逻辑。

多个slave是怎么交互的

  当有消息需要发送给集群服务时,首先先判断是否为本机服务消息,如果是集群服务的,先将消息插入到本机的service_harbor服务当中,然后再service_harbor进行分发处理,如果目标slave未完成与本机的连接建立,那么先将消息插入到目标slave的消息队列当中,等待目标slave与本机完成连接时,再对消息队列的消息分发处理。

master主要起到什么功能

  master服务在此中的作用,主要是监听各slave的消息,有新slave连接上来时会通知集群中的slave与新slave建立连接。另外会管理全局服务的创建与映射等。

service_harbor在实现时可能出现什么问题

  service_harbor 在实现时,可能会出现目标节点仍未建立连接完成,就有消息要发送给目标节点,这时需要先让这些消息保存起来,等连接建立完成后,再处理对应消息。

该模式的隐患

  该模式主要是为了解决单机cpu运算能力不足的补充,所以对集群的支持可能没那么完善。该模式默认集群中的所有节点都是正常工作并且稳定通讯的,否则整个集群工作都会出现问题。

posted @   小乐虎  阅读(592)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示