本文以 skynet 示例 simpledb 为例,讲述 skynet 创建 lua 服务的流程
首先 skynet 中使用 skynet.newservice 来创建 lua 服务
1 skynet.newservice("simpledb")
1 --@name:"simpledb"2 --@args: nil3 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 --@_:address2 --@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 中一定有一个问号,问号最后被替换为 name11 ????int sz = path_size + name_size;12 ????//search path13 ????void * dl = NULL;14 ????char tmp[sz];15 ????do16 ????{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_init10 ????mod->init = dlsym(mod->module, tmp);11 ????strcpy(tmp+name_size, "_release"); ?????// snlua_release12 ????mod->release = dlsym(mod->module, tmp);13 ????strcpy(tmp+name_size, "_signal"); ??????// snlua_signal14 ????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: ??????snlua3 --@inst: ???struct snlua4 --@ctx: 5 --@parm: ???"simpledb"6 int7 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 message17 ????skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz);18 ????return 0;19 }
1 --@ud: ?????struct snlua *2 --@cb: ?????launch_cb3 void4 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 void3 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 服务流程
原文地址:https://www.cnblogs.com/wjl-blog/p/9134760.html