基本信息初始化
初始化配置之后,就要开始启动skynet的主要模块。不过,启动模块之前要先初始化一些基本信息。
- 生成harbor ID。这个ID用一个整型的高八位表示,也就是说master/harbor模式中,同一个集群最多只有256个harbor服务器。
- 句柄池初始化。这个句柄池主要用来管理服保存务对象的句柄,其中还包括句柄和服务的映射关系,这个句柄具有唯一新(harbor模式下在集群都是唯一的),因此只要知道地址(句柄)你就可以快速找到对应的服务对象。还有skynet采用的是Actor模型,所以这个服务对象其实对应的就是Actor对象。
- 一级队列初始化。skynet的消息处理采用的是两级消息队列方式,二级消息队列保存在服务对象上,就是说每个服务对象有独立的消息队列。
- module接口初始化。这个modules类型的全局变量主要保存了加载到skynet中c库的基本信息以及对外提供调用c库的方法。这个可以看作面向对象范畴里面的接口对象,每个加载的c库可以认为是继承了这个接口的类的实例化,所有加载的c库对象都是通过这个接口调用。
- 定时器模块初始化。
- 网络模块初始化。这里主要是创建一个管道,作为服务对象和网络IO的桥梁从避免IO阻塞对服务器对象的影响。后续讲到服务调度就会知道服务阻塞的危害
下面是skynet_start文件中的代码,加上了简略的注释:
//初始化基本信息
skynet_harbor_init(config->harbor); //生成harbor ID 高八位表示 所以master/harbor模式下同个集群里最多只有256台harbor
skynet_handle_init(config->harbor); //句柄池初始化,管理服务对象句柄(地址),以及地址和服务的映射关系。
skynet_mq_init(); //一级消息队列初始化(skynet采用的是两级消息队列模式)
skynet_module_init(config->module_path); // module接口,这里主要存储加载到skynet中的c库,并对外提供c库方法的接口。这个可以看做是一个接口类,每加载一个c库相当于实例化一个继承该接口的对象。
skynet_timer_init(); // 定时器模块初始化。
skynet_socket_init(); // 网络模块初始化,这个并非skynet的网络库,这里主要是创建一个管道,作为服务对象和网络IO的桥梁从避免IO阻塞对服务器对象的影响。
skynet_profile_enable(config->profile); // debug
优先启动的服务
日志服务:主要负责输出与记录,程序运行中的日志。通用引导服务:用于拉起其他的服务。
struct skynet_context *ctx = skynet_context_new(config->logservice, config->logger); // 生成日志服务Actor
bootstrap(ctx, config->bootstrap); //生成通用lua引导服务Actor
监视器
create_thread(&pid[0], thread_monitor, m); //启动监视模块
监视器的作用监控工作模块(其中的工作线程)对可能出现的死循环通过打印信息发出警告。下面是monitor的数据结构。
struct skynet_monitor {
int version; // 版本号 -- 其实就是消息的流水号
int check_version; // 检查版本号
uint32_t source; // 源地址
uint32_t destination; // 目标地址
};
监视器的工作方式,工作线程每次处理消息之前,version自增,记录源地址(source)和目标地址(destination),然后5秒钟检测一次目标地址是否为空(处理完消息之后置空源地址)且version与check_version相等则提示该工作线程可能死循环(因为该消息已经占用工作线程5秒)。
thread_monitor中每次检查间隔5秒。
for (i=0;i<5;i++) {
// 休息5秒
CHECK_ABORT
sleep(1);
}
监视器的消息版本号更新与检测。
void
skynet_monitor_trigger(struct skynet_monitor *sm, uint32_t source, uint32_t destination) {
sm->source = source;
sm->destination = destination;
ATOM_INC(&sm->version);
}
void
skynet_monitor_check(struct skynet_monitor *sm) {
if (sm->version == sm->check_version) {
// 版本一致
if (sm->destination) {
skynet_context_endless(sm->destination); //检查目标服务的引用,引用为0则回收,释放工作线程
skynet_error(NULL, "A message from [ :%08x ] to [ :%08x ] maybe in an endless loop (version = %d)", sm->source , sm->destination, sm->version);
}
} else {
sm->check_version = sm->version; // 不一致的时候更新
}
}
定时器模块
create_thread(&pid[1], thread_timer, m); //启动定时器模块
skynet的主要模块之一,为服务器提供基础的定时器功能。skynet的定时器是通过计数器的方式实现的,把时间段分成5级,一级为时间近点,即将发生超时的时间段,等待通知处理。后面4级是时间远点,随着时间增长一级一级向前移动直至近点,等待处理。下面是主要的数据结构:
//定时事件
struct timer_event {
uint32_t handle; // 服务地址
int