首先会调用 skynet.newservice(“test”)
接口文件: skynet.lua
name:test
arg:…
function skynet.newservice(name, ...)
return skynet.call(".launcher", "lua" , "LAUNCH", "snlua", name, ...)
end
此接口将会给 launcher 服务发送一个lua消息 命令是 LAUNCH 后面的都是参数
接口文件: *** launcher.lua***
cmd LAUNCH
*** … 参数列表***
skynet.dispatch("lua", function(session, address, cmd , ...)
cmd = string.upper(cmd)
local f = command[cmd]
if f then
local ret = f(address, ...)
if ret ~= NORET then
skynet.ret(skynet.pack(ret))
end
else
skynet.ret(skynet.pack {"Unknown command"} )
end
end)
接下来会调用
cmd snlua
*** … 参数列表***
function command.LAUNCH(_, service, ...)
launch_service(service, ...)
return NORET
end
cmd snlua
*** … 参数列表***
local function launch_service(service, ...)
local param = table.concat({...}, " ") --打包参数
local inst = skynet.launch(service, param) --调用skynet.launch
...
end
接口文件: *** manager.lua***
function skynet.launch(...)
local addr = c.command("LAUNCH", table.concat({...}," ")) --打包参数 调用c层接口
if addr then
return tonumber("0x" .. string.sub(addr , 2))
end
end
接口文件: *** lua-skynet.c***
LUAMOD_API int
luaopen_skynet_core(lua_State *L) {
luaL_checkversion(L);
luaL_Reg l[] = {
{ "send" , lsend },
{ "genid", lgenid },
{ "redirect", lredirect },
{ "command" , lcommand }, //调用lcommand
{ "intcommand", lintcommand },
{ "addresscommand", laddresscommand },
{ "error", lerror },
{ "harbor", lharbor },
{ "callback", lcallback },
{ "trace", ltrace },
{ NULL, NULL },
};
...
return 1;
}
static int
lcommand(lua_State *L) {
struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));
const char * cmd = luaL_checkstring(L,1);
const char * result;
const char * parm = NULL;
if (lua_gettop(L) == 2) {
parm = luaL_checkstring(L,2);
}
result = skynet_command(context, cmd, parm); //cmd LAUNCH parm 参数
if (result) {
lua_pushstring(L, result);
return 1;
}
return 0;
}
接口文件: *** skynet_server.c***
static struct command_func cmd_funcs[] = {
{ "TIMEOUT", cmd_timeout },
{ "REG", cmd_reg },
{ "QUERY", cmd_query },
{ "NAME", cmd_name },
{ "EXIT", cmd_exit },
{ "KILL", cmd_kill },
{ "LAUNCH", cmd_launch }, //发现调用的是cmd_launch
{ "GETENV", cmd_getenv },
{ "SETENV", cmd_setenv },
{ "STARTTIME", cmd_starttime },
{ "ABORT", cmd_abort },
{ "MONITOR", cmd_monitor },
{ "STAT", cmd_stat },
{ "LOGON", cmd_logon },
{ "LOGOFF", cmd_logoff },
{ "SIGNAL", cmd_signal },
{ NULL, NULL },
};
//cmd LAUNCH
const char *
skynet_command(struct skynet_context * context, const char * cmd , const char * param) {
struct command_func * method = &cmd_funcs[0];
while(method->name) {
if (strcmp(cmd, method->name) == 0) {
return method->func(context, param); //调用上面的cmd_launch
}
++method;
}
return NULL;
}
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 = "test"
struct skynet_context * inst = skynet_context_new(mod,args);
if (inst == NULL) {
return NULL;
} else {
id_to_hex(context->result, inst->handle);
return context->result;
}
}
struct skynet_context *
skynet_context_new(const char * name, const char *param) {
// 获取 snlua 模块
struct skynet_module * mod = skynet_module_query(name); //name = snlua param = test
if (mod == NULL)
return NULL;
// 创建一个空服务
void *inst = skynet_module_instance_create(mod);
if (inst == NULL)
return NULL;
struct skynet_context * ctx = skynet_malloc(sizeof(*ctx));
CHECKCALLING_INIT(ctx)
//下面是对 ctx的初始化 省略
...
// 服务初始化
int r = skynet_module_instance_init(mod, inst, ctx, param);
...
}
接口文件: *** skynet_module.c***
struct skynet_module *
skynet_module_query(const char * name) {
// 这里我们假设 snlua 模块已注册,然后就找到 snlua 模块
struct skynet_module * result = _query(name);
if (result)
return result;
SPIN_LOCK(M)
result = _query(name); // double check
if (result == NULL && M->count < MAX_MODULE_TYPE) {
int index = M->count;
void * dl = _try_open(M,name);
if (dl) {
M->m[index].name = name;
M->m[index].module = dl;
if (open_sym(&M->m[index]) == 0) {
M->m[index].name = skynet_strdup(name);
M->count ++;
result = &M->m[index];
}
}
}
SPIN_UNLOCK(M)
return result;
}
// 查询 snlua(snlua.so) 模块是否已加载,未加载就进行加载准备工作
static struct skynet_module *
_query(const char * name) {
int i;
for (i=0;i<M->count;i++) {
if (strcmp(M->m[i].name,name)==0) {
return &M->m[i];
}
}
return NULL;
}
static void *
_try_open(struct modules *m, const char * name) {
const char *l;
// config 文件中的配置 cpath = "./cservice/?.so"
const char * path = m->path;
size_t path_size = strlen(path);
size_t name_size = strlen(name);
// 这里为什么不是 sz = path_size + name_size + 1,因为有效 path 中一定有一个问号,问号最后被替换为 name
int sz = path_size + name_size;
//search path
void * dl = NULL;
char tmp[sz];
do
{
memset(tmp,0,sz);
while (*path == ';') path++;
if (*path == '\0') break;
l = strchr(path, ';');
if (l == NULL) l = path + strlen(path);
int len = l - path;
// 将 '?' 替换为文件名 tmp = "./cservice/snlua.so"
int i;
for (i=0;path[i]!='?' && i < len ;i++) {
tmp[i] = path[i];
}
memcpy(tmp+i,name,name_size);
if (path[i] == '?') {
strncpy(tmp+i+name_size,path+i+1,len - i - 1);
} else {
fprintf(stderr,"Invalid C service path\n");
exit(1);
}
// 打开动态库
dl = dlopen(tmp, RTLD_NOW | RTLD_GLOBAL);
path = l;
}while(dl == NULL);
if (dl == NULL) {
fprintf(stderr, "try open %s failed : %s\n",name,dlerror());
}
return dl;
}
static int
open_sym(struct skynet_module *mod) {
mod->create = get_api(mod, "_create");// snlua_create
mod->init = get_api(mod, "_init");// snlua_init
mod->release = get_api(mod, "_release");// snlua_release
mod->signal = get_api(mod, "_signal");// snlua_signal
return mod->init == NULL;
}
static void *
get_api(struct skynet_module *mod, const char *api_name) {
size_t name_size = strlen(mod->name); // mod->name = "snlua"
size_t api_size = strlen(api_name);
char tmp[name_size + api_size + 1];
memcpy(tmp, mod->name, name_size);
memcpy(tmp+name_size, api_name, api_size+1);
char *ptr = strrchr(tmp, '.');
if (ptr == NULL) {
ptr = tmp;
} else {
ptr = ptr + 1;
}
return dlsym(mod->module, ptr);
}
void *
skynet_module_instance_create(struct skynet_module *m) {
if (m->create) {
return m->create(); // 调用 snlua_create()
} else {
return (void *)(intptr_t)(~0);
}
}
接口文件: *** service_snlua.c***
struct snlua *
snlua_create(void) {
struct snlua * l = skynet_malloc(sizeof(*l));
memset(l,0,sizeof(*l));
l->mem_report = MEMORY_WARNING_REPORT;
l->mem_limit = 0;
l->L = lua_newstate(lalloc, l);
return l;
}
struct snlua {
lua_State * L;
struct skynet_context * ctx;
size_t mem;
size_t mem_report;
size_t mem_limit;
};
接口文件: *** skynet_module.c***
int
skynet_module_instance_init(struct skynet_module *m, void * inst, struct skynet_context *ctx, const char * parm) {
return m->init(inst, ctx, parm); // 调用 snlua_init()
}
接口文件: *** service_snlua.c***
snlua_init(struct snlua *l, struct skynet_context *ctx, const char * args) {
int sz = strlen(args);
char * tmp = skynet_malloc(sz);
memcpy(tmp, args, sz);
// 注册新服务的 callback 函数为 launch_cb,用于服务的初始化(实际上就是加载服务脚本)
skynet_callback(ctx, l , launch_cb);
const char * self = skynet_command(ctx, "REG", NULL);
uint32_t handle_id = strtoul(self+1, NULL, 16);
// 向新服务发送初始化消息 tmp = "test",实际就是调用 launch_cb 且 msg = "test"
// it must be first message
skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz);
return 0;
}
接口文件: *** skynet_server.c***
void
skynet_callback(struct skynet_context * context, void *ud, skynet_cb cb) {
context->cb = cb;
context->cb_ud = ud;
}
int
skynet_send(struct skynet_context * context, uint32_t source, uint32_t destination , int type, int session, void * data, size_t sz) {
if ((sz & MESSAGE_TYPE_MASK) != sz) {
skynet_error(context, "The message to %x is too large", destination);
if (type & PTYPE_TAG_DONTCOPY) {
skynet_free(data);
}
return -1;
}
_filter_args(context, type, &session, (void **)&data, &sz);
if (source == 0) {
source = context->handle;
}
if (destination == 0) {
return session;
}
if (skynet_harbor_message_isremote(destination)) {
struct remote_message * rmsg = skynet_malloc(sizeof(*rmsg));
rmsg->destination.handle = destination;
rmsg->message = data;
rmsg->sz = sz & MESSAGE_TYPE_MASK;
rmsg->type = sz >> MESSAGE_TYPE_SHIFT;
skynet_harbor_send(rmsg, source, session);
} else {
struct skynet_message smsg;
smsg.source = source;
smsg.session = session;
smsg.data = data;
smsg.sz = sz;
// 将初始化消息放入服务的消息队列
if (skynet_context_push(destination, &smsg)) {
skynet_free(data);
return -1;
}
}
return session;
}
接口文件: *** service_snlua.c***
static int
launch_cb(struct skynet_context * context, void *ud, int type, int session, uint32_t source , const void * msg, size_t sz) {
assert(type == 0 && session == 0);
struct snlua *l = ud;
// 清除服务的 callback 函数,因为 launch_cb 的使命已经完成,callback 函数将在 skynet.start 中被覆盖为 skynet.dispatch_message
skynet_callback(context, NULL, NULL);
// 进行服务初始化,调用 config 文件中配置的 lualoader(默认是 loader.lua) 加载对应的服务 msg = "test"
int err = init_cb(l, context, msg, sz);
if (err) {
skynet_command(context, "EXIT", NULL);
}
return 0;
}
static int
init_cb(struct snlua *l, struct skynet_context *ctx, const char * args, size_t sz) {
...
// 加载 config 中配置的 lualoader 对应的文件
const char * loader = optstring(ctx, "lualoader", "./lualib/loader.lua");
int r = luaL_loadfile(L,loader);
if (r != LUA_OK) {
skynet_error(ctx, "Can't load %s : %s", loader, lua_tostring(L, -1));
report_launcher_error(ctx);
return 1;
}
// 目标服务名压栈 args = "test"
lua_pushlstring(L, args, sz);
// 执行 lualoader 中的代码
r = lua_pcall(L,1,0,1);
...
return 0;
}
接口文件: *** skynet_start.c***
static void *
thread_worker(void *p) {
struct worker_parm *wp = p;
int id = wp->id;
int weight = wp->weight;
struct monitor *m = wp->m;
struct skynet_monitor *sm = m->m[id];
skynet_initthread(THREAD_WORKER);
struct message_queue * q = NULL;
while (!m->quit) {
// worker 线程从消息队里中取出消息并分发给服务执行
q = skynet_context_message_dispatch(sm, q, weight);
...
}
return NULL;
}
接口文件: *** skynet_server.c***
struct message_queue *
skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue *q, int weight) {
...
dispatch_message(ctx, &msg);
...
}
static void
dispatch_message(struct skynet_context *ctx, struct skynet_message *msg) {
...
// 这里调用的 ctx->cb 就是 launch_cb, msg->data = "test"
reserve_msg = ctx->cb(ctx, ctx->cb_ud, type, msg->session, msg->source,
...
}