skynet_启动lua服务

Skynet学习

skynet启动lua服务
每个skynet进程在启动的时候,都会启动一个lua层的launcher服务器,该服务主要负责skynet的运作期间进行创建其他lua服务.
launcher是在 bootstrap.lua中启动的, skynet.launch(“snlua”,“launcher”)) 这里启动,关于
skynet.launch方法,后面会与描述.

在skynet.lua中 skynet.newservice(),代码如下

 function skynet.newservice(name, ...) --启动一个LUA服务,实际上是给launcher发送消息实现的
     return skynet.call(".launcher", "lua" , "LAUNCH", "snlua", name, ...) 
 end

调用skynet.lua中 skynet.call()

function skynet.call(addr,typename,...) --阻塞发送消息
	local p = proto[typename] --现根据名字取得对应的消息类别(table)
	local session = c.send(addr,p.id,nil,p.pack(...)) --p.pack打包消息,然后发送消息,保存返回的会话
	if session == nil then
		error("call to invalid address" .. skynet.address(addr))
	end
	return p.unpack(yield_call(addr,session)) --挂起调用,解包返回的消息,消息大小
end

c.send 从lua-skynet.c的 luaopen_skynet_core中对应的函数名跟函数发生调用,send对应
lsend函数,lsend调用send_message

static int send_message(lua_State *L, int source,int idx_type){
	struct skynet_context* context = lua_touserdata(L,lua_upvalueindex(1)); //从伪索引获取上下文
	uint32_t dest = lua_tounsigned(L,1); //获取目标数字地址
	const char* dest_string = NULL; //目标字符串地址
	if(dest == 0) { //传入的是数字,而不是字符串
		dest_string = get_dest_string(L,1); //获取目标字符串地址
	}
	int type = luaL_checkinteger(L,2); //获取消息类型
	int session = 0;
	if (lua_isnil(L,3) { //如果第三个参数是空,则是从skynet.call调用过来的
		type |= PTYPE_TAG_ALLOCESSION; //skynet.call会在内部生成一个唯一session,所以这里需要设置分配会话标志
	}else{
		session = luaL_checkinteger(L,3); //获取会话
		//skynet.send调用过来的传递的是0
	}
	int mtype == lua_type(L,4); //取得第四个参数的类型
	switch(mtype) { //判断类型
	case LUA_TSTRING:{ //如果是字符串类型
		size_t len = 0;  //存放字符串长度
		void* msg = (void*)lua_tolstring(L,4,&len); //获取字符串
		if (len == 0 ) msg = NULL; 置空
		if (dest_string){ //目标是字符串地址
			session = sknet_sendname(context,0,dest_string,type,session,msg,len); 	
		}esle {
			session = skynet_send(context,0,dest,type,session,msg,len);
		}
		break;
	}
	case LUA_TLIGHTUSERDATA: { //用户数据
		void * msg = lua_touserdata(L,4); //获取消息c指针
		int size = luaL_checkinteger(L,5); //获取消息长度
		         if (dest_string) {//如果目标是字符串地址
             session = skynet_sendname(context, 0, dest_string, type | PTYPE_TAG_DONTCOPY, session, msg, size);//不需要拷贝消息数据
         } else {//如果目标是数字地址
             session = skynet_send(context, 0, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size);//不需要拷贝消息数据
         }   
         break;
	}	
	default:
		luaL_error(L,"seynet.send invalid param %s", lua_typename(L, lua_type(L,4)));
	}
	if (session < 0 ) {
	         // send to invalid address
         // todo: maybe throw error whould be better
         // 发送到无效的地址
         // 可能抛出一个错误更好
         return 0;	
	}
    lua_pushinteger(L,session);//返回会话
    return 1;
}

这里调用skynet_server.c中的skynet_send()方法

int skynet_send(struct_context* context,uint32_t source, uint32_t destination, int type, int session, void* data, size_t sz) {
	if ((sz & HANDLE_MASK) != sz) { //判断消息是否过大
		skynet_error(context,"The message to %x is too large (sz=%lu)",destination,sz);
		skynet_free(data);
		return -1;
	}
	_filter_args(context,type,&session,(void*)&data,&sz); //过滤参数
	if(source == 0 ) { //源为0
		source = context->handle; //设置源为自己
	}
	if (destination == 0){ //目的地址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;
		skynet_harbor_send(rmsg,source,session); //用harbor讲消息发出去
	}else { //本地消息
		struct skynet_message smsg; //定义一个skynet消息
		smsg.source = source;
		smsg.session = session;
		smsg.data = data;
		smsg.sz = sz;
		//发送消息到目标的队列
		if (skyet_context_push(destination,&smsg)) {
			skynet_free(data); //push失败,释放数据
			return -1;
		}
	}	
	return session; //返回会话
}

这里的skynet_server.c中的skynet_context_push

int skynet_context_push(uint32_t handle, struct skynet_message* message){
	struct skynet_context* ctx= skynet_handle_grab(handle); //根据句柄获取上下文引用
	if (ctx == NULL) {
		return -1;
	}
	skynet_mq_push(ctx->queue,message); //将消息放入队列
	skynet_context_release(ctx); //释放上下文,因为在skynet_handle_grab中调用了skynet_context_grab增加了上下文的引用计数
	return 0;
}

这里调用到skynet_mq.c中的skynet_mq_push()

void skynet_mq_push(struct message_queue* q,struct skynet_message* message){
	assert(message);
	SPIN_LOCK(q);
	q->queue[q->tail] == *message; 
	if (++q->tail > q->cap){
		q->tail = 0;
	}
	if (q->head == q->tail) {
		expand_queue(q);
	}
	if (q->in_global == 0) {
		q->in_global = 	MQ_IN_GLOBAL;
		skynet_golbalmq_push(q);
	}
	SPIN_UNLOCK(q);
}

这就是启动一个服务的逻辑就是向launcher的服务队列中发送了需要启动的服务name,接下来就看launchar服务是如何启动一个服务的.
launcher服务收到消息后,就去处理,调用launcher.lua中的launch_service()方法

local function launch_service(service,...)
	local param = table.concat({...}, " ")
	local inst = skynet.launch(service,param) --启动服务
	local response = skynet.response()
	if inst then
		services[inst] = service .. " " .. param --保存服务名 参数
		instance[inst] = response --保存闭包
	else
		reponse(false) -- 启动失败,抛出异常
		return 	
	end
	return inst 
end

这里调用manager.lua中的sknet.launch方法

function skynet.launch(...)
	local addr = c.command("LAUNCH", table.concat({...}," "))
	if addr then
		return tonumber("0x" .. string.sub(addr,2))
	end
end

这里则用调用到lua-skynet.c中的luaopen_skynet_core对应的方法 command字段对应lcommand方法

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* parm = NULL; //参数
	if (lua_gettop(L) == 2) { //传入的参数有2个
		parm = luaL_checkstring(L,2); //取出参数串
	}
	result = skynet_command(context,cmd,parm); //执行命令
	if (result) {
		lua_pushstring(L,result); //压入返回值
		return 1; //有一个返回值
	}
	return 0; //没有返回值
}

这里调到skynet_server.c中的skynet_command()方法,改方法通过字段名执行不同的方法

 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 },
     { "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 },
 };

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);
		}
		++method;
	}
	return NULL;
}

根据传入的LAUNCHER参数,得知对应的方法为skynet_server.c 中的 cmd_launch,

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"); //模块名
	args = strsep(&args, "\r\n"); //参数
	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; //返回字符串(句柄的十六进制)
	}
}

到这里,就是调用最开始的skynet_context_new()方法来创建一个服务

你可能感兴趣的:(skynet)