@wsd1
2017-01-19T07:57:00.000000Z
字数 3712
阅读 2664
skynet
http://cloudwu.github.io/lua53doc/manual.html#6.4.1
https://github.com/cloudwu/skynet/wiki
从 Commits on Apr 13, 2014 开始,skynet开始添加了 lua版本的gate实现,版本号是 8e92930413e8e18f55f389e93d522614c88eb4c6。
如果想要看到早期无干扰版本,可以提取前一个commit:
https://github.com/cloudwu/skynet/tree/8e92930413e8e18f55f389e93d522614c88eb4c6
文件 见:service-src/service_gate.c
关于gate的说明,细节可见:
http://blog.codingnow.com/2012/09/the_design_of_skynet.html
云风设计思路:
无论是哪种模式,控制信息都是交给 watchdog 去处理的,而数据包如果不发给 watchdog 而是发送给 agent 或 broker的话,则不会有额外的数据头(也减少了数据拷贝)。
识别这些包是从外部发送进来的方法是检查消息包的类型是否为 PTYPE_CLIENT 。
C版本的service总是被编译成.so文件。需要时被加载,通过create函数被创建,通过init函数被初始化(安装callback),通过release函数被释放。
service_gate.c中,三个函数分别是gate_create(), gate_init(), gate_release()。
代码框架如下:
int
gate_init(struct gate *g , struct skynet_context * ctx, char * parm) {
...
int n = sscanf(parm, "%c %s %s %d %d", &header, watchdog, binding, &client_tag, &max);
//这里通过参数获取配置信息
...
//下面是一个skynet address(id)到connect的hash映射
hashid_init(&g->hash, max);
g->conn = skynet_malloc(max * sizeof(struct connection));
...
//安装回调函数
skynet_callback(ctx,g,_cb);
//listen
return start_listen(g,binding);
}
static int
_cb(struct skynet_context * ctx, void * ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
struct gate *g = ud;
switch(type) {
case PTYPE_TEXT:
_ctrl(g , msg , (int)sz);
break;
case PTYPE_CLIENT: {
...
uint32_t uid = idbuf[0] | idbuf[1] << 8 | idbuf[2] << 16 | idbuf[3] << 24;
int id = hashid_lookup(&g->hash, uid);
if (id>=0) {
// don't send id (last 4 bytes)
skynet_socket_send(ctx, uid, (void*)msg, sz-4);
return 1; // return 1 means don't free msg
}
...
}
case PTYPE_SOCKET:
// recv socket message from skynet_socket
dispatch_socket_message(g, msg, (int)(sz-sizeof(struct skynet_socket_message)));
break;
}
return 0;
}
可见其处理三种消息,text、client和socket类型。一一分析:
“PTYPE_TEXT”:主要用于接收本地指令。
static void
_ctrl(struct gate * g, const void * msg, int sz) {
struct skynet_context * ctx = g->ctx;
...
if (memcmp(command,"kick",i)==0) {
...
}
if (memcmp(command,"forward",i)==0) {
...
_forward_agent(g, id, agent_handle, client_handle);
return;
}
if (memcmp(command,"broker",i)==0) {
...
g->broker = skynet_queryname(ctx, command);
return;
}
if (memcmp(command,"start",i) == 0) {
...
skynet_socket_start(ctx, uid);
return;
}
if (memcmp(command, "close", i) == 0) {
...
skynet_socket_close(ctx, g->listen_id);
...
}
skynet_error(ctx, "[gate] Unkown command : %s", command);
}
"kick"、“start”,"close"这类的指令功能明确毋庸质疑。我们主要看一下forward指令的用法。
详见:"gate_server的forward原理分析" 章节
开头已经说明agent模式中,gate会将数据直接发给agent,这里的主要设置这个关系。在lua中对应的调用类如:
\service\watchdog.lua,line14:
if agent then
agent_all[self] = { agent , client }
skynet.send(gate, "text", "forward" , self, skynet.address(agent) , skynet.address(client))
end
//上述代码判断是否有agent这个参数,若是有则关联agent。
注意:后期的skynet版本都开始使用lua版本的gate_server,所以找不到使用场景,只要利用下面的指令回归一下老版本:
git checkout 52cf86403735d4f64ab2ff5458d9a2c743388ae5
可以回溯到早期版本, “service_gate.c”这个文件刚刚加入,可以在watchdog.lua中看到使用方式。
可见:
lua代码:
skynet.send(gate, "text", "forward" , self, skynet.address(agent) , skynet.address(client))
对应
c代码:
static void
_forward_agent(struct gate * g, int id, uint32_t agentaddr, uint32_t clientaddr) {
struct connection * agent = lookup_id(g,id);
if (agent) {
agent->agent = agentaddr;
agent->client = clientaddr;
}
}
代码很简单,通过connection_id找到connection结构,并赋值agent和client。之后的事情就是在“socket”事件处理函数中发生的,其依次调用: dispatch_message() -> _forward()
_forward()中:
...
if (c->agent) {
void * temp = malloc(c->header);
read_data(g,c,temp, c->header);
skynet_send(ctx, c->client, c->agent, g->client_tag | PTYPE_TAG_DONTCOPY, 0 , temp, c->header);
//注意上面用clinet的addr来设置source字段,就是为了假装是client发出的。
//另外,发出的msg类型也是client类型
}
else if (g->watchdog) {
char * tmp = malloc(c->header + 32);
int n = snprintf(tmp,32,"%d data ",c->id);
read_data(g,c,tmp+n,c->header);
skynet_send(ctx, 0, g->watchdog, PTYPE_TEXT | PTYPE_TAG_DONTCOPY, 0, tmp, c->header + n);
//这个就是用9表示source,类型也是TEXT了。
}