1. 程式人生 > >skynet 創建 lua 服務流程

skynet 創建 lua 服務流程

table service send value don 如果 rto tco 獲取

本文以 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 }

skynet 創建 lua 服務流程