hrefspace

 找回密码
 立即注册
搜索
热搜: PHP PS 程序设计
查看: 907|回复: 5

从模块上研究skynet的源码

[复制链接]

502

主题

502

帖子

1536

积分

大司空

Rank: 5Rank: 5

积分
1536
发表于 2024-1-11 00:15:01 | 显示全部楼层 |阅读模式
skynet各层表现



从上大概就清楚skynet的“内部”
而skynet源码目录结构如下:
3rd:第三方代码,有lua和jemalloc等。
lualib:使用lua写的库
lualib-src:使用C写并封装给lua使用的库
service:使用lua写的skynet的服务模块
service-src:使用C写的skynet的服务模块
skynet-src:skynet核心代码

skynet重要模块gate.so:为整个skynet提供socket功能
snlua.so:启动多个lua服务,skynet自带的模块中有一个重要的模块是snlua.so模块,通过snlua.so和指定lua脚本文件可以启动多个lua编写的服务,不用每个服务都是用c来编写,而且大部分逻辑都是在 lua 脚本下开发,只有需要考虑性能的模块才用 C 语言开发成库,直接提供给 lua 调用。
logger.so:日志模块,一个简单的日志系统,可以用来记录服务的相关信息。
harbar.so:(集群模块)节点服务,每个Skynet运行都是一个节点

skynet重要服务launcher.lua:在lua中启动服务
skynet.lua:lua常用功能封装
skynet.so:lua调用skynet功能

skynet重要文件skynet_server.c:管理服务
skynet_handle.c:管理服务唯一的handle
skynet_module.c:启动c编写的so模块
skynet_monitor.c:监视服务死循环
skynet_mq.c:消息队列
skynet_timer.c:定时器
skynet_socket.c:Socket
skynet_master.c:不同skynet节点服务名字中心服务
skynet_harbor.c:不同skynet节点通讯


skynet底层代码位于skynet/skynet-src下,模块加载相关在skynet-module.c skynet-module.h这两个文件里。这里的模块在linux下指的是so,在windows下指的是dll,在skynet中指的是config中配置的cpath下的文件。


每个模块需要实现四个最基本的函数,create/init/release/signal。create做内存分配。init做初始化,它可能会做一些其它的事情,比如打开网络,打开文件,函数回调挂载等等。relase做资源回收,包括内存资源,文件资源,网络资源等等,signal是发信号,比如kill信号,告诉模块该停了。

网络网络部分是一个服务器最基础最核心的部分,这个技术也已经是非常成熟了,现在已经很少有人自己实现一个网络相关的库了。skynet的网络库是自己实现的。
实际上云风只实现了epoll和kqueue,windows上的变种请自行搜索吧。

epoll和kqueue的实现分别在skynet_epoll.h和epoll_kqueue.h当中。epoll的函数其实就是epoll_create/epoll_ctl/epoll_del/epoll_wait这几个,要注意的是skynet中的epoll_create的参数是1024。所以连接数上不去的话很可能就是这里限制了。
skynet在skynet_poll.h中根据平台的不同包含了不同的头文件,屏蔽了平台相关性。然后在socket_server.c中实现了网络服务的逻辑。
然后skynet在skynet_socket.c中对socket_server.c中的逻辑再次做了一个封装,还添加了socket客户端相关的函数,就是connect/send/close之类的函数。
为了方便lua层使用socket,在lua-socket.c中再将对skynet_socket.c进行了一次封装。这个封装就是c语言层和lua语言层的相互转换。目前只支持tcp和udp,基于tcp上的http/websocket之类统统是不支持的。


为什么操作一个网络要费这么大的劲呢,绕来绕去非常的不直观。因为skynet是基于消息的,而且每个服务都有一个monitor,每个消息处理的时候要尽可能的短,这样才不会阻塞服务里其它的请求。而connect这种明显是阻塞的,当然也可以写成非阻塞的,但是非阻塞的话,你需要不断地挂起,因为非阻塞实际上是基于select技术来实现的。而不断地挂起,这个就很麻烦,写起来很痛苦而且很容易出错。因此云风把这些都放到网络线程中来做,这样就不会影响工作线程。但是这样做也有它的缺点,那就是网络线程可能会被阻塞,网络线程被阻塞就会导致服务无响应。或者导致大量的数据包积累,引起波峰。

skynet的配置加载

skynet的配置文件是以lua格式来写的。使用过skynet的都清楚skynet的启动命令是skynet config_file_name。配置文件名是作为命令行参数传给skynet进程的。
skynet进程启动以后,会读取config文件,然后解析这个lua文件。然后把相关的配置信息设置到lua的环境变量里。
C层读取配置的话是要从lua环境变量里去取的。


skynet一共有4种线程,monitor线程用于检测节点内的消息是否堵住,timer线程运行定时器,socket线程进行网络数据的收发,worker线程则负责对消息队列进行调度(worker线程的数量,可以通过配置表指定)。消息调度规则是,每条worker线程,每次从全局消息队列中pop出一个次级消息队列,并从次级消息队列中pop出一条消息,并找到该次级消息队列的所属服务,将消息传给该服务的callback函数,执行指定业务,当逻辑执行完毕时,再将次级消息队列push回全局消息队列中。因为每个服务只有一个次级消息队列,每当一条worker线程,从全局消息队列中pop出一个次级消息队列时,其他线程是拿不到同一个服务,并调用callback函数,因此不用担心一个服务同时在多条线程内消费不同的消息,一个服务执行,不存在并发,线程是安全的
socket线程、timer线程甚至是worker线程,都有可能会往指定服务的次级消息队列中push消息,push函数内有加一个自旋锁,避免同时多条线程同时向一个次级消息队列push消息的惨局。


在2021年1月13/14号我会开一个四小时玩转skynet训练营
现在已经开放报名,感兴趣的诸位同好可以订阅一下,
训练营内容大概如下:
1.  多核并发编程
2.  消息队列,线程池
3.  actor消息调度
4.  网络模块实现
5.  时间轮定时器实现
6.  lua/c接口编程
7.  skynet编程精要
8.  demo演示actor编程思维
期待与诸位同好共襄技术盛举
凭借报名截图可以进群973961276领取上一期skynet训练营的录播以及这期的预习资料哦!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

0

主题

161

帖子

2

积分

新手上路

Rank: 1

积分
2
发表于 2024-1-11 00:15:21 | 显示全部楼层
已报名,麻烦通过一下群
回复

使用道具 举报

0

主题

180

帖子

2

积分

新手上路

Rank: 1

积分
2
发表于 2024-1-11 00:15:48 | 显示全部楼层
写的不错 已加
回复

使用道具 举报

0

主题

171

帖子

2

积分

新手上路

Rank: 1

积分
2
发表于 2024-1-11 00:16:09 | 显示全部楼层
感谢楼主
回复

使用道具 举报

0

主题

199

帖子

71

积分

关内侯

Rank: 2

积分
71
发表于 2024-1-11 00:16:47 | 显示全部楼层
真不错  报名了
回复

使用道具 举报

0

主题

190

帖子

170

积分

关内侯

Rank: 2

积分
170
发表于 2024-1-11 00:17:07 | 显示全部楼层
学费了,学废了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|hrefspace

GMT+8, 2024-5-10 19:26 , Processed in 0.074668 second(s), 22 queries .

Powered by hrefspace X3.4 Licensed

Copyright © 2022, hrefspace.

快速回复 返回顶部 返回列表