skynet 创建lua服务流程

本文将介绍 skynet 怎么去创建一个lua 服务服务 ,以一个简单的test服务为例

首先会调用 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, 
		...
}

你可能感兴趣的:(skynet框架分析)