skynet源码分析(6)--消息机制之消息分发
来源:互联网 发布:苹果手机变音软件 编辑:程序博客网 时间:2024/05/22 15:31
作者:shihuaping0918@163.com,转载请注明作者
第5篇讲到了消息的处理,消息的处理实际上就是对工作队列里的消息不停地调回调函数。那么消息是怎么放进消息队列的呢。带着这个疑问,让我们从lua层开始追根溯源。
在lua层有两个api,一个是skynet.send,这个是非阻塞发消息。另一个是skynet.call,这个是阻塞式发完消息等回应。skynet.call使用一个session来实现等待,这个session实际就是一个自增的数字,溢出了以后又从1开始。
以skynet.send为例进行分析。skynet.send位于skynet/lualib/skynet.lua文件中。
function skynet.send(addr, typename, ...) local p = proto[typename] return c.send(addr, p.id, 0 , p.pack(...)) -- c就是skynet.coreend
可以看出skynet.send实际上是调了skynet.core里的send函数。而skynet.core的定义在skynet/lualib-src/lua-skynet.c这个文件中。因为下面有一部分代码涉及到lua c api,这部分我不打算说明了。
LUAMOD_API intluaopen_skynet_core(lua_State *L) { //这个就是skynet.core luaL_checkversion(L); luaL_Reg l[] = { { "send" , lsend }, //这个就是send对应的函数lsend { "genid", lgenid }, 省略..... { NULL, NULL }, };}
从上面的代码可以知道,skynet.core.send实际对应的是lsend函数。
static intlsend(lua_State *L) { return send_message(L, 0, 2);}
而lsend又调的是send_message函数。
static intsend_message(lua_State *L, int source, int idx_type) {略... switch (mtype) { case LUA_TSTRING: { if (dest_string) { session = skynet_sendname(context, source, dest_string, type, session , msg, len); } else { session = skynet_send(context, source, dest, type, session , msg, len); } break; } case LUA_TLIGHTUSERDATA: { void * msg = lua_touserdata(L,idx_type+2); int size = luaL_checkinteger(L,idx_type+3); if (dest_string) { session = skynet_sendname(context, source, dest_string, type | PTYPE_TAG_DONTCOPY, session, msg, size); } else { session = skynet_send(context, source, dest, type | PTYPE_TAG_DONTCOPY, session, msg, size); } break; } default: luaL_error(L, "invalid param %s", lua_typename(L, lua_type(L,idx_type+2))); } 略...}
从上面代码看出,最终调的是skynet_sendname和skynet_send,而skynet_sendname实际上是调用的skynet_send。
intskynet_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; 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;}
看到这里终于看到消息结构体了。
intskynet_context_push(uint32_t handle, struct skynet_message *message) { struct skynet_context * ctx = skynet_handle_grab(handle); //增加ctx引用计数 if (ctx == NULL) { return -1; } skynet_mq_push(ctx->queue, message); //消息入队完成 skynet_context_release(ctx); //减少ctx引用计数 return 0;}
从上面的代码分析可以很清楚地看到,skynet.send实际上就是往目标服务的消息队列里增加一条消息。
在这篇文章里,消息处理涉及到了lua c api,这个东西不是一两篇文章能说得清楚的。所以直接略过了。
从代码跟踪的过程来看,发送一个消息实际上要进行6层函数调用。消息最终才能投到目标服务的消息队列中。而消息队列是怎么被处理的,在第5篇中已经讲过了。剩下的就是,消息的回调到底是怎么被注册的。这个将在下一篇中讲到。
- skynet源码分析(6)--消息机制之消息分发
- skynet源码分析(5)--消息机制之消息处理
- skynet源码分析(10)--消息机制之消息注册和回调
- skynet消息队列源码分析
- skynet源码分析(2)--消息队列mq
- skynet源码分析(3)--消息名字和ID之handle
- apollo 消息分发源码分析
- 消息机制源码分析
- ceph源码分析之消息通信机制
- Android源码分析之Handler消息机制
- Handler消息机制源码分析
- android消息机制源码分析
- Android 消息机制源码分析
- Android消息机制源码分析
- Android消息机制源码分析
- Android 消息机制源码分析
- 异步消息机制源码分析
- 异步消息机制源码分析
- skynet源码分析(2)--消息队列mq
- skynet源码分析(3)--消息名字和ID之handle
- Unix和Linux有什么区别? 通俗解释
- skynet源码分析(4)--monitor
- skynet源码分析(5)--消息机制之消息处理
- skynet源码分析(6)--消息机制之消息分发
- skynet源码分析(7)--skynet中的timer
- Java-约瑟夫问题(Josephus Problem)
- Launcher3 源码阅读之step7:下载Google官方最新的Launcher3源码并导入到Android Studio
- 合并两个排序的链表
- 文件夹、文件夹内容拷贝
- jQuery最佳实践
- [机器学习入门] 李宏毅机器学习课程回顾 + 接下来的学习声明
- war反编译成java项目