Skynet集群cluster发送机制分析

cluster模块提供了跨进程节点通讯功能。进而有产生一些疑问出来。比如节点间发送消息是怎么样被发送的?涉及到哪些服务?消息是否有大小限制?, 这里记录下阅读笔记

  1. 每个节点自身的监听是通过复用的方式实现,创建内置gate监听处理socket.
  2. 消息的发送者服务client,发送消息的起源就是调用了内置cluster.lua模块。对每个远程消息都总是根据目标去cluseterd服务查询负责对应目标节点发送任务的clustersend服务句柄(本地服务也会缓存来减少节点内的跨服务查询)。
  3. 把cluster层消息(一序列的Lua变量)通过skynet.pack接口(对应C层luaseri_pack接口)打包。skynet.pack接口当前有一个限制是最多只能打包32层深度的table。这个pack操作会遍历需要打包的变量列表。使用C层内置一个缓存管理来序列化(位于lualib-src/lua-seri.c)。这个缓存管理实现是通过把每个128字节内存块连成一个链表,即以128字节为单位进行内存管理。随着不停的遍历,不断增加小内存愉块。最近就会把消息变量列表序列化到这个链表中。这个缓存管理的第1个节点是栈的分配的,于是可以做到发送小于128字节数据时可以不需要经过skynet_malloc动态分配内存。。到此为止,这些序列化好的链表不不能直接发送,还需要通过遍历链表碎片内存,合并到一个使用skynet_malloc分配的完整内存块后当作lightuserdata A返回给Lua层并释放链表动态内存。所以skynet.pack内存成本,就是序列化成本和内存复制成本。
  4. 把打包的内存用skynet.send发送到对应的clustersender服务. 即把前面说到的完整一块A和其他参数再次打包复制一次(Lua协议的打包函数也是skynet.pack),打包结果B插入到clusersender服务的消息队列。这里也产生了一次内存复制(把A打包为B时,使用浅复制,参考lualib-src/lua-seri.c:wb_pointer函数),同时还有消息队列入消息的读写锁和自旋锁操作成本。
  5. clustersender服务收到并处理这个消息,回调Lua层函数,因为最外层是Lua协议,因此进行第一次skynet.unpack得到命令变量和A,然后在cluster层逻辑中通过packpush或者packrequest把数据A复制转化为cluster数据格式C并释放A。其中如果A大于32KB,则分包。
  6. 把序列化好的cluster数据C交给socketchannel发送。数据C在网络发送后C层进行释放。函数返回后在C层dispatch_message函数释放这个B(此时A已经在前面释放了)
  7. 目标节点的clusteragent分收到消息并解包,根据要求调用对应的服务。

转载自:https://www.ankergame.cn

posted @ 2022-04-21 10:18  2006年的夏天  阅读(238)  评论(0编辑  收藏  举报