skynet学习笔记 源码之skynet_server服务api

前言

skynet_server是skynetc层的核心。代码主要实现skynet.h和skynet_server.h的api函数。

skynet.h主要提供了消息发送,消息回调函数注册等等。

void skynet_error(struct skynet_context * context, const char *msg, ...);
const char * skynet_command(struct skynet_context * context, const char * cmd , const char * parm);
uint32_t skynet_queryname(struct skynet_context * context, const char * name);
int skynet_send(struct skynet_context * context, uint32_t source, uint32_t destination , int type, int session, void * msg, size_t sz);
int skynet_sendname(struct skynet_context * context, uint32_t source, const char * destination , int type, int session, void * msg, size_t sz);

int skynet_isremote(struct skynet_context *, uint32_t handle, int * harbor);

typedef int (*skynet_cb)(struct skynet_context * context, void *ud, int type, int session, uint32_t source , const void * msg, size_t sz);
void skynet_callback(struct skynet_context * context, void *ud, skynet_cb cb);

uint32_t skynet_current_handle(void);
uint64_t skynet_now(void);
void skynet_debug_memory(const char *info);	// for debug use, output current service memory to stderr

skynet_server.h主要提供了对context的一系列操作,比如创建,销毁等待。


struct skynet_context * skynet_context_new(const char * name, const char * parm);
void skynet_context_grab(struct skynet_context *);
void skynet_context_reserve(struct skynet_context *ctx);
struct skynet_context * skynet_context_release(struct skynet_context *);
uint32_t skynet_context_handle(struct skynet_context *);
int skynet_context_push(uint32_t handle, struct skynet_message *message);
void skynet_context_send(struct skynet_context * context, void * msg, size_t sz, uint32_t source, int type, int session);
int skynet_context_newsession(struct skynet_context *);
struct message_queue * skynet_context_message_dispatch(struct skynet_monitor *, struct message_queue *, int weight);	// return next queue
int skynet_context_total();
void skynet_context_dispatchall(struct skynet_context * context);	// for skynet_error output before exit

void skynet_context_endless(uint32_t handle);	// for monitor

void skynet_globalinit(void);
void skynet_globalexit(void);
void skynet_initthread(int m);

void skynet_profile_enable(int enable);

context结构

struct skynet_context {
	void * instance;               //c_moudle _create函数返回的实例指针
	struct skynet_module * mod;    //c_moudle 的指针,方便调用模块,create,init,signal,release接口
	void * cb_ud;                  //skynet_call_back设置的回调函数数据指针
	skynet_cb cb;                  //skynet_call_back设置的回调函数指针
	struct message_queue *queue;   //消息队列
	ATOM_POINTER logfile;          //日志文件
	uint64_t cpu_cost;	// in microsec
	uint64_t cpu_start;	// in microsec
	char result[32];               //执行cmd函数的结果
	uint32_t handle;               //context句柄唯一id
	int session_id;                //消息session_id用于对应消息回复
	ATOM_INT ref;                  //引用计数,当引用计数为0时,可以释放context内存
	int message_count;             //统计消息处理总数
	bool init;                     //是否初始化
	bool endless;                  //是否出现单个消息处理太久
	bool profile;                  //是否需要统计消息处理cpu占用时间

	CHECKCALLING_DECL              //自旋锁
};

context实例的创建

struct skynet_context * 
skynet_context_new(const char * name, const char *param) {
	struct skynet_module * mod = skynet_module_query(name); //查询加载c服务动态库

	if (mod == NULL)
		return NULL;

	void *inst = skynet_module_instance_create(mod);        //调用c服务创建函数接口
	if (inst == NULL)
		return NULL;
	struct skynet_context * ctx = skynet_malloc(sizeof(*ctx));
	CHECKCALLING_INIT(ctx)

	ctx->mod = mod;
	ctx->instance = inst;
	ATOM_INIT(&ctx->ref , 2);
	ctx->cb = NULL;
	ctx->cb_ud = NULL;
	ctx->session_id = 0;
	ATOM_INIT(&ctx->logfile, (uintptr_t)NULL);

	ctx->init = false;
	ctx->endless = false;

	ctx->cpu_cost = 0;
	ctx->cpu_start = 0;
	ctx->message_count = 0;
	ctx->profile = G_NODE.profile;
	// Should set to 0 first to avoid skynet_handle_retireall get an uninitialized handle
	ctx->handle = 0;	
	ctx->handle = skynet_handle_register(ctx);
	struct message_queue * queue = ctx->queue = skynet_mq_create(ctx->handle);
	// init function maybe use ctx->handle, so it must init at last
	context_inc();

	CHECKCALLING_BEGIN(ctx)
	int r = skynet_module_instance_init(mod, inst, ctx, param); //调用c服务init接口,通常init函数会设置好回调函数
	CHECKCALLING_END(ctx)
	if (r == 0) {
		struct skynet_context * ret = skynet_context_release(ctx);
		if (ret) {
			ctx->init = true;
		}
		skynet_globalmq_push(queue);
		if (ret) {
			skynet_error(ret, "LAUNCH %s %s", name, param ? param : "");
		}
		return ret;
	} else {
		skynet_error(ctx, "FAILED launch %s", name);
		uint32_t handle = ctx->handle;
		skynet_context_release(ctx);
		skynet_handle_retire(handle);
		struct drop_t d = { handle };
		skynet_mq_release(queue, drop_message, &d);
		return NULL;
	}
}

cmd命令

command接口包装了一系列命令

static struct command_func cmd_funcs[] = {
	{ "TIMEOUT", cmd_timeout },     //注册定时器任务
	{ "REG", cmd_reg }              //设置别名
	{ "QUERY", cmd_query },         //通过别名查询handle_id
	{ "NAME", cmd_name },           //设置handle_id的别名
	{ "EXIT", cmd_exit },           //安全退出
	{ "KILL", cmd_kill },           //强制杀掉一个服务
	{ "LAUNCH", cmd_launch },       //拉起一个服务
	{ "GETENV", cmd_getenv },       //获取环境变量
	{ "SETENV", cmd_setenv },       //设置环境变量
	{ "STARTTIME", cmd_starttime }, //进程开始时间戳
	{ "ABORT", cmd_abort },         //退出进程
	{ "MONITOR", cmd_monitor },     //设置监控退出服务id
	{ "STAT", cmd_stat },           //提供获取 mqlen,endless,cpu,message命令
	{ "LOGON", cmd_logon },         //消息日志打开
	{ "LOGOFF", cmd_logoff },       //消息日志关闭
	{ "SIGNAL", cmd_signal },       //调用moudle的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;
}

消息处理

skynet对消息处理不是说有多少消息就一次性处理完了,每个线程都会安排一个权重weight

当weight大于0的时候,n会取到 len / 2^weight,一次会处理掉n条消息,这样就保证了消息量大的服务不会处理不过来。

当权重为-1,该线程每次只处理一条消息,确保其他服务消息有人处理。

struct message_queue * 
skynet_context_message_dispatch(struct skynet_monitor *sm, struct message_queue *q, int weight) {
	if (q == NULL) {
		q = skynet_globalmq_pop();
		if (q==NULL)
			return NULL;
	}

	uint32_t handle = skynet_mq_handle(q);

	struct skynet_context * ctx = skynet_handle_grab(handle);
	if (ctx == NULL) {
		struct drop_t d = { handle };
		skynet_mq_release(q, drop_message, &d);
		return skynet_globalmq_pop();
	}

	int i,n=1;
	struct skynet_message msg;

	for (i=0;i= 0) {
			n = skynet_mq_length(q);
			n >>= weight;
		}
		int overload = skynet_mq_overload(q);
		if (overload) {
			skynet_error(ctx, "May overload, message queue length = %d", overload);
		}

		skynet_monitor_trigger(sm, msg.source , handle);

		if (ctx->cb == NULL) {
			skynet_free(msg.data);
		} else {
			dispatch_message(ctx, &msg);
		}

		skynet_monitor_trigger(sm, 0,0);
	}

	assert(q == ctx->queue);
	struct message_queue *nq = skynet_globalmq_pop();
	if (nq) {
		// If global mq is not empty , push q back, and return next queue (nq)
		// Else (global mq is empty or block, don't push q back, and return q again (for next dispatch)
		skynet_globalmq_push(q);
		q = nq;
	} 
	skynet_context_release(ctx);

	return q;
}

	static int weight[] = { 
		-1, -1, -1, -1, 0, 0, 0, 0,
		1, 1, 1, 1, 1, 1, 1, 1, 
		2, 2, 2, 2, 2, 2, 2, 2, 
		3, 3, 3, 3, 3, 3, 3, 3, };

weight是对应的线程的权重,当有8个线程时,线程的权重为-1,-1,-1,-1,0,0,0,0。

当权重为0,该线线程会处理全部消息,n = len >> weight = len / 2^weight

当权重为1,该线线程会一半消息,n = len >> weight = len / 2^weight

当权重为2,该线线程会4分之一消息,n = len >> weight = len / 2^weight

当权重为3,该线线程会8分之一消息,n = len >> weight = len / 2^weight

可见这样设计兼顾了深度和广度。

你可能感兴趣的:(skynet,多线程)