skynet 创建 lua 服务流程

本文以 skynet 示例 simpledb 为例,讲述 skynet 创建 lua 服务的流程
首先 skynet 中使用 skynet.newservice 来创建 lua 服务
  1 skynet.newservice("simpledb") 
1 --@name:"simpledb"
2 --@args: nil
3 function skynet.newservice(name, ...)
4     return skynet.call(".launcher", "lua" , "LAUNCH", "snlua", name, ...)
5 end
-- 实际调用为 skynet.call(".launcher", "lua" , "LAUNCH", "snlua", "simpledb")
1 --@cmd:"LAUNCH"
2 --@args: "snlua", "simpledb"
3 skynet.dispatch("lua", function(session, address, cmd , ...)
4     cmd = string.upper(cmd)
5     local f = command[cmd]
6     ...
7     local ret = f(address, ...)
8     ...
9 end)
-- 实际调用为 command.LAUNCH(address, "snlua", "simpledb")
1 --@_:address
2 --@service:"snlua"
3 --@args: "simpledb"
4 function command.LAUNCH(_, service, ...)
5     launch_service(service, ...)
6 end
-- 实际调用为 launch_service("snlua", "simpledb")
1 --@service:"snlua"
2 --@args: "simpledb"
3 local function launch_service(service, ...)
4     local param = table.concat({...}, " ")  -- param = "simpledb"
5     local inst = skynet.launch(service, param)
6     -- 实际调用为 skynet.launch("snlua", "simpledb")
7 end
1 --@args: "snlua", "simpledb"
2 function skynet.launch(...)
3     local addr = c.command("LAUNCH", table.concat({...}," "))
4 end
-- 实际调用为 c.command("LAUNCH", "snlua simpledb")
 1 --@cmd: "LAUNCH"
 2 --@parm: "snlua simpledb"
 3 static int
 4 lcommand(lua_State *L) {
 5     struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));
 6     const char * cmd = luaL_checkstring(L,1);
 7     const char * result;
 8     const char * parm = NULL;
 9     if (lua_gettop(L) == 2) {
10         parm = luaL_checkstring(L,2);
11     }
12     result = skynet_command(context, cmd, parm);
13     ...
14 }
 1 --@cmd: "LAUNCH"
 2 --@parm: "snlua simpledb"
 3 const char *
 4 skynet_command(struct skynet_context * context, const char * cmd , const char * param) {
 5     struct command_func * method = &cmd_funcs[0];
 6     while(method->name) {
 7         if (strcmp(cmd, method->name) == 0) {
 8             return method->func(context, param);
 9         }
10         ++method;
11     }
12     return NULL;
13 }
-- 实际调用为 cmd_launch(context, "snlua simpledb");
--@parm: "snlua simpledb"
static const char *
cmd_launch(struct skynet_context * context, const char * param) {
    size_t sz = strlen(param);
    char tmp[sz+1];
    strcpy(tmp,param);
    char * args = tmp;
    char * mod = strsep(&args, " \t\r\n");  // mod = "snlua"
    args = strsep(&args, "\r\n");           // args = "simpledb"
    struct skynet_context * inst = skynet_context_new(mod, args);
    ...
}
-- 实际调用为 skynet_context_new("snlua", "simpledb");
 1 --@name: "snlua"
 2 --@param: "simpledb"
 3 struct skynet_context *
 4 skynet_context_new(const char * name, const char *param) {
 5     // 获取 snlua 模块
 6     struct skynet_module * mod = skynet_module_query(name);
 7     ...
 8     // 创建一个空服务
 9     void *inst = skynet_module_instance_create(mod);
10     ...
11     // 服务初始化
12     int r = skynet_module_instance_init(mod, inst, ctx, param);
13     ...
14 }
-- 这里主要是获取 snlua(snlua.so) 模块,如果模块尚未加载就先加载
1 --@name: "snlua"
2 struct skynet_module *
3 skynet_module_query(const char * name) {
4     // 这里我们假设 snlua 模块已注册,然后就找到 snlua 模块
5     struct skynet_module * result = _query(name);
6     ...
7 }
-- 查询 snlua(snlua.so) 模块是否已加载,未加载就进行加载准备工作
 1 --@name: "snlua"
 2 static struct skynet_module *
 3 _query(const char * name) {
 4     int i;
 5     for (i=0;i<M->count;i++) {
 6         if (strcmp(M->m[i].name,name)==0) {
 7             return &M->m[i];
 8         }
 9     }
10     return NULL;
11 }
 1 --@name: "snlua"
 2 static void *
 3 _try_open(struct modules *m, const char * name) {
 4     const char *l;
 5     // config 文件中的配置 cpath = "./cservice/?.so"
 6     const char * path = m->path;
 7     size_t path_size = strlen(path);
 8     size_t name_size = strlen(name);
 9 
10     // 这里为什么不是 sz = path_size + name_size + 1,因为有效 path 中一定有一个问号,问号最后被替换为 name
11     int sz = path_size + name_size;
12     //search path
13     void * dl = NULL;
14     char tmp[sz];
15     do
16     {
17         memset(tmp,0,sz);
18         while (*path == ';') path++;
19         if (*path == '\0') break;
20         l = strchr(path, ';');
21         if (l == NULL) l = path + strlen(path);
22       int len = l - path;
23  
24      // 将 '?' 替换为文件名 tmp = "./cservice/snlua.so"
25         int i;
26         for (i=0;path[i]!='?' && i < len ;i++) {
27             tmp[i] = path[i];
28         }
29         memcpy(tmp+i,name,name_size);
30         if (path[i] == '?') {
31             strncpy(tmp+i+name_size,path+i+1,len - i - 1);
32         } else {
33             fprintf(stderr,"Invalid C service path\n");
34             exit(1);
35         }
36 
37         // 打开动态库
38         dl = dlopen(tmp, RTLD_NOW | RTLD_GLOBAL);
39         path = l;
40     }while(dl == NULL);
41 
42     if (dl == NULL) {
43         fprintf(stderr, "try open %s failed : %s\n",name,dlerror());
44     }
45 
46     return dl;
47 }
 1 static int
 3 _open_sym(struct skynet_module *mod) {
 4     size_t name_size = strlen(mod->name);   // mod->name = "snlua"
 5     char tmp[name_size + 9]; // create/init/release/signal , longest name is release (7)
 6     memcpy(tmp, mod->name, name_size);
 7     strcpy(tmp+name_size, "_create");       // snlua_create
 8     mod->create = dlsym(mod->module, tmp);
 9     strcpy(tmp+name_size, "_init");         // snlua_init
10     mod->init = dlsym(mod->module, tmp);
11     strcpy(tmp+name_size, "_release");      // snlua_release
12     mod->release = dlsym(mod->module, tmp);
13     strcpy(tmp+name_size, "_signal");       // snlua_signal
14     mod->signal = dlsym(mod->module, tmp);
15 
16     return mod->init == NULL;
17 }
18 
19 void *
20 skynet_module_instance_create(struct skynet_module *m) {
21     if (m->create) {
22         return m->create();     // 调用 snlua_create()
23     } else {
24         return (void *)(intptr_t)(~0);
25     }
26 }
 1 struct snlua *
 3 snlua_create(void) {
 4     struct snlua * l = skynet_malloc(sizeof(*l));
 5     memset(l,0,sizeof(*l));
 6     l->mem_report = MEMORY_WARNING_REPORT;
 7     l->mem_limit = 0;
 8     l->L = lua_newstate(lalloc, l);
 9     return l;
10 }
1 struct snlua {
3     lua_State * L;
4     struct skynet_context * ctx;
5     size_t mem;
6     size_t mem_report;
7     size_t mem_limit;
8 };
1 --@m:       snlua
3 --@inst:    struct snlua
4 --@ctx: 
5 --@parm:    "simpledb"
6 int
7 skynet_module_instance_init(struct skynet_module *m, void * inst, struct skynet_context *ctx, const char * parm) {
8     return m->init(inst, ctx, parm);    // 调用 snlua_init(inst, ctx, parm)
9 }
 1 --@args:    "simpledb"
 3 int
 4 snlua_init(struct snlua *l, struct skynet_context *ctx, const char * args) {
 5     int sz = strlen(args);
 6     char * tmp = skynet_malloc(sz);
 7     memcpy(tmp, args, sz);
 8     
 9     // 注册新服务的 callback 函数为 launch_cb,用于服务的初始化(实际上就是加载服务脚本)
10     skynet_callback(ctx, l , launch_cb);
11 
12     const char * self = skynet_command(ctx, "REG", NULL);
13     uint32_t handle_id = strtoul(self+1, NULL, 16);
14 
15     // 向新服务发送初始化消息 tmp = "simpledb",实际就是调用 launch_cb 且 msg = "simpledb"
16     // it must be first message
17     skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz);
18     return 0;
19 }
1 --@ud:      struct snlua *
2 --@cb:      launch_cb
3 void
4 skynet_callback(struct skynet_context * context, void *ud, skynet_cb cb) {
5     context->cb = cb;
6     context->cb_ud = ud;
7 }
 1 --@data:    "simpledb"
 3 int
 4 skynet_send(struct skynet_context * context, uint32_t source, uint32_t destination , int type, int session, void * data, size_t sz) {
 5     ...
 6     struct skynet_message smsg;
 7     smsg.source = source;
 8     smsg.session = session;
 9     smsg.data = data;
10     smsg.sz = sz;
11     // 将初始化消息放入服务的消息队列
12     skynet_context_push(destination, &smsg)
13 }
 1 static int
 3 launch_cb(struct skynet_context * context, void *ud, int type, int session, uint32_t source , const void * msg, size_t sz) {
 4     ...
 5     struct snlua *l = ud;
 6 
 7     // 清除服务的 callback 函数,因为 launch_cb 的使命已经完成,callback 函数将在脚本中使用 skynet.dispatch 重新写入
 8     skynet_callback(context, NULL, NULL);
 9 
10     // 进行服务初始化,调用 config 文件中配置的 lualoader(默认是 loader.lua) 加载对应的服务 msg = "simpledb"
11     int err = init_cb(l, context, msg, sz);
12     ...
13 }
 1 static int
 3 init_cb(struct snlua *l, struct skynet_context *ctx, const char * args, size_t sz) {
 4     lua_State *L = l->L;
 5     l->ctx = ctx;
 6     ...
 7     lua_pushlightuserdata(L, ctx);
 8     lua_setfield(L, LUA_REGISTRYINDEX, "skynet_context");
 9 
10     // 加载 config 中配置的 lualoader 对应的文件
11     const char * loader = optstring(ctx, "lualoader", "./lualib/loader.lua");
12     int r = luaL_loadfile(L,loader);
13     ...
14     // 目标服务名压栈 args = "simpledb"
15     lua_pushlstring(L, args, sz);
16 
17     // 执行 lualoader 中的代码
18     r = lua_pcall(L,1,0,1);
19     ...
20 }
 1 static void *
 3 thread_worker(void *p) {
 4     ...
 5     struct message_queue * q = NULL;
 6     while (!m->quit) {
 7         // worker 线程从消息队里中取出消息并分发给服务执行
 8         q = skynet_context_message_dispatch(sm, q, weight);
 9         ...
10     }
11     return NULL;
12 }
1 struct message_queue *
3 skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue *q, int weight) {
4     ...
5     dispatch_message(ctx, &msg);
6     ...
7 }
1 static void
3 dispatch_message(struct skynet_context *ctx, struct skynet_message *msg) {
4     ...
5     // 这里调用的 ctx->cb 就是 launch_cb, msg->data = "simpledb"
6     reserve_msg = ctx->cb(ctx, ctx->cb_ud, type, msg->session, msg->source, msg->data, sz);
7     ...
8 }

猜你喜欢

转载自www.cnblogs.com/wjl-blog/p/9134760.html