Skynet_基本概况

Skynet基本概况

工作流程

主要工作流程图

Skynet_Flowchar

上图中所示的每个队列都关联一个服务的。所有的队列连接成一个大链表。

一个工作线程主要工作流程:

1
2
3
4
5
6
7
while(true)
{
queue = get_one_queue(global_queue) //通过全局队列拿到一个私有队列
ctx = get_ctx( queue->handle ) //通过队列拿到一个服务
msg = get_one_message(queue) //从队列里面取出一个消息
ctx->callback(msg) //调用服务的回调函数 ,在这个回调函数里面再调用一个固定的 lua函数
}

一个skynet节点,也就是一个skynet进程。它启动的时候会创建多个线程:

  • 网络线程:
    • 接收skynet外部的网络请求,然后push到服务的队列。
    • 当服务需要把数据发送给外部时,实质上是 服务—>网络线程—>外部
  • 定时器线程:把定时器消息push到某个服务。当服务在处理定时器消息时,就可以认为是定时器事件触发了。
  • 监听线程:主要是发现工作线程有没有死循环。
  • 工作线程:驱动服务处理消息

主函数代码

skynet_main函数

main函数读取了一个 lua配置文件。这个配置文件就是启动 skynet进程时使用的命令,类似 ./skynet config 。 这里的config就是配置文件。Skynet_Config官方文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int
main(int argc, char *argv[]) {
const char * config_file = NULL ;
skynet_globalinit();
skynet_env_init();

sigign();

struct skynet_config config;

struct lua_State *L = luaL_newstate();//生成luastate的目的主要是读取配置文件中的配置项
luaL_openlibs(L); // link lua lib

int err = luaL_loadbufferx(L, load_config, strlen(load_config), "=[skynet config]", "t");
assert(err == LUA_OK);
lua_pushstring(L, config_file);

err = lua_pcall(L, 1, 1, 0);//执行配置文件

_init_env(L);
//把配置文件里面的配置项 都读出来
config.thread = optint("thread",8);
config.module_path = optstring("cpath","./cservice/?.so");//默认路径下的动态链接库有gate.so harbor.so logger.so snlua.so 常用snlua.so来创建模块实例
config.harbor = optint("harbor", 1);//这里推荐在配置文件中设置为0 否则默认为1
config.bootstrap = optstring("bootstrap","snlua bootstrap");//启动服务设置: snlua作为服务模块 bootstrap作为服务对应的lua文件
config.daemon = optstring("daemon", NULL);//后台运行配置
config.logger = optstring("logger", NULL);//保存日志的文件名字 这里日志的来源是lua层调用的skynet.error(str)
config.logservice = optstring("logservice", "logger");//默认日志模块是 logger 即 service_logger.c 所代表的模块
config.profile = optboolean("profile", 1);

lua_close(L);

skynet_start(&config);//next
skynet_globalexit();

return 0;
}

skynet_start函数

初始化工作和启动两个服务——bootstrap(ctx, config->bootstrap)start(config->thread)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void 
skynet_start(struct skynet_config * config) {

skynet_harbor_init(config->harbor);
skynet_handle_init(config->harbor);//可以简单的认为就是保存了 handle和服务 映射关系的数组
skynet_mq_init();//初始化一个全局队列 用来把所有服务关联的队列 连接起来
skynet_module_init(config->module_path);//模块初始化
skynet_timer_init();
skynet_socket_init();

skynet_profile_enable(config->profile);
struct skynet_context *ctx = skynet_context_new(config->logservice, config->logger);//首先创建日志服务

skynet_handle_namehandle(skynet_context_handle(ctx), "logger");//给日志服务绑定一个名字

bootstrap(ctx, config->bootstrap);//创建bootstrp服务 注意这里服务并没有完成所有流程 因为工作线程还没有启动 服务的第一个消息只是push进队列了
start(config->thread);//next
}

start函数

启动:监听线程、定时器线程、网络线程和多个工作线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static void
start(int thread) {
pthread_t pid[thread+3];

struct monitor *m = skynet_malloc(sizeof(*m));//创建监听管理器
memset(m, 0, sizeof(*m));
m->count = thread;//数量跟工作线程数一致 工作线程数一般又设置成跟你的cpu核心数相同
m->sleep = 0;

m->m = skynet_malloc(thread * sizeof(struct skynet_monitor *));
int i;
for (i=0;i<thread;i++) {
m->m[i] = skynet_monitor_new();//每个工作线程安排一个监听
}

create_thread(&pid[0], thread_monitor, m);//监听线程的职责是监听工作线程
create_thread(&pid[1], thread_timer, m);//定时器线程
create_thread(&pid[2], thread_socket, m);//网络线程

struct worker_parm wp[thread];
for (i=0;i<thread;i++) {
create_thread(&pid[i+3], thread_worker, &wp[i]);//给每个工作线程传递不同参数
}
}