skynet 創建 lua 服務流程
阿新 • • 發佈:2018-06-04
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-- 實際調用為 command.LAUNCH(address, "snlua", "simpledb")--@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)
1 --@_:address-- 實際調用為 launch_service("snlua", "simpledb")2 --@service:"snlua" 3 --@args: "simpledb" 4 function command.LAUNCH(_, service, ...) 5 launch_service(service, ...) 6 end
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 服務流程