前面四篇已经涵盖了skynet的c层核心,剩下的timer,socket模块本身和actor模型没什么关系,且比较独立,最后再看吧。光用skynet的c接口,是很难在这上面写业务逻辑的,所以要找一种更爽快的方式来使用。官方推荐的是lua,利用lua的协程对skynet的消息分发做了封装,使得actor之间的异步消息通信有同步一样的操作感,并且做了一些的扩展模块来方便使用。lua简洁实用的风格我个人也很钟意。

要想做一个lua binding来使用,要有两个必要条件:

  1. 根据skynet的模块契约实现一个动态库作为lua的宿主。
  2. 将skynet的公开接口封装成一个lua库。

以下用skynet-lua代指官方的lua binding


宿主

先来想一想这个宿主要干些什么?它起码要完成三件事:

  1. 创建一个lua虚拟机
  2. 加载执行一个lua服务的启动脚本
  3. 将回调的数据传递给lua层

skynet-lua的宿主的叫snlua,就是干这三件事的,其实现在/service-src/service_snlua.c中,直接来看四个契约函数吧:

1 struct snlua *
2 snlua_create(void) {
3     struct snlua * l = skynet_malloc(sizeof(*l));
4     memset(l,0,sizeof(*l));
5     l->mem_report = MEMORY_WARNING_REPORT;
6     l->mem_limit = 0;
7     l->L = lua_newstate(lalloc, l);
8     return l;
9 }

create函数创建了一个lua vm,且注入了自己的分配器,为了使用jemalloc,获取vm的内存量,限制vm内存上限。

再来看看init函数:

 1 int
 2 snlua_init(struct snlua *l, struct skynet_context *ctx, const char * args) {
 3     int sz = strlen(args);
 4     char * tmp = skynet_malloc(sz);
 5     memcpy(tmp, args, sz);
 6     skynet_callback(ctx, l , launch_cb);
 7     const char * self = skynet_command(ctx, "REG", NULL);
 8     uint32_t handle_id = strtoul(self+1, NULL, 16);
 9     // it must be first message
10     skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz);
11     return 0;
12 }

注册了回调,向自己发送一个消息,然后什么也没做,看来真正的初始化是推迟到了第一条消息的处理上做了。

来看看回调函数吧:

 1 static int
 2 launch_cb(struct skynet_context * context, void *ud, int type, int session, uint32_t source , const void * msg, size_t sz) {
 3     assert(type == 0 && session == 0);
 4     struct snlua *l = ud;
 5     skynet_callback(context, NULL, NULL);
 6     int err = init_cb(l, context, msg, sz);
 7     if (err) {
 8         skynet_command(context, "EXIT", NULL);
 9     }
10
11     return 0;
12 }

初始化在init_cb,若失败就关闭自己,看看init_cb:

 1 static int
 2 init_cb(struct snlua *l, struct skynet_context *ctx, const char * args, size_t sz) {
 3     lua_State *L = l->L;
 4     l->ctx = ctx;
 5     lua_gc(L, LUA_GCSTOP, 0);
 6     lua_pushboolean(L, 1);  /* signal for libraries to ignore env. vars. */
 7     lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
 8     luaL_openlibs(L);
 9     lua_pushlightuserdata(L, ctx);
10     lua_setfield(L, LUA_REGISTRYINDEX, "skynet_context");
11     luaL_requiref(L, "skynet.codecache", codecache , 0);
12     lua_pop(L,1);
13
14     const char *path = optstring(ctx, "lua_path","./lualib/?.lua;./lualib/?/init.lua");
15     lua_pushstring(L, path);
16     lua_setglobal(L, "LUA_PATH");
17     const char *cpath = optstring(ctx, "lua_cpath","./luaclib/?.so");
18     lua_pushstring(L, cpath);
19     lua_setglobal(L, "LUA_CPATH");
20     const char *service = optstring(ctx, "luaservice", "./service/?.lua");
21     lua_pushstring(L, service);
22     lua_setglobal(L, "LUA_SERVICE");
23     const char *preload = skynet_command(ctx, "GETENV", "preload");
24     lua_pushstring(L, preload);
25     lua_setglobal(L, "LUA_PRELOAD");
26
27     lua_pushcfunction(L, traceback);
28     assert(lua_gettop(L) == 1);
29
30     const char * loader = optstring(ctx, "lualoader", "./lualib/loader.lua");
31
32     int r = luaL_loadfile(L,loader);
33     if (r != LUA_OK) {
34         skynet_error(ctx, "Can't load %s : %s", loader, lua_tostring(L, -1));
35         report_launcher_error(ctx);
36         return 1;
37     }
38     lua_pushlstring(L, args, sz);
39     r = lua_pcall(L,1,0,1);
40     if (r != LUA_OK) {
41         skynet_error(ctx, "lua loader error : %s", lua_tostring(L, -1));
42         report_launcher_error(ctx);
43         return 1;
44     }
45     lua_settop(L,0);
46     if (lua_getfield(L, LUA_REGISTRYINDEX, "memlimit") == LUA_TNUMBER) {
47         size_t limit = lua_tointeger(L, -1);
48         l->mem_limit = limit;
49         skynet_error(ctx, "Set memory limit to %.2f M", (float)limit / (1024 * 1024));
50         lua_pushnil(L);
51         lua_setfield(L, LUA_REGISTRYINDEX, "memlimit");
52     }
53     lua_pop(L, 1);
54
55     lua_gc(L, LUA_GCRESTART, 0);
56
57     return 0;
58 }

step1:25行前,保存sc,加载配置项(config里的lua_path,lua_cpath,lua_service等)。

step2:45行前,加载了一个lua的加载器脚本,用它来设置这些配置项,并运行入口脚本,各配置项含义如下:

  lua_service:lua服务(actor)的搜索路径,与lua_path的语义一致,不占用lua本身的lua_path,但有一个小行为,当服务名不是一个文件名,而是一个目录名时,会把目录加入lua_path,也就是搜索路径为:/?/main.lua,服务名为foo,会把/foo/?.lua加入lua_path。这样为了同服务之间的脚本加载方便。服务名来自init_cb的args,args[0]为服务名,后续为入口脚本的参数.

  lua_path,lua_cpath与lua本身的语义一致,加载器仅仅将它们赋给package.path,package.cpath.

  lua_preload:如果指定,则在运行入口脚本前会先运行它。

之所以要用一个lua加载器来间接运行入口脚本,主要是为了实现起来方便。加载器最后还会写入两个全局变量:SERVICE_NAME(服务名),SERVICE_PATH(服务目录)

step3:看看入口脚本有没有设置memlimit(vm内存上限),如果有就设置到snlua_ud,这样vm在分配内存时就可以做判断。这个参数也只能在这个时机里设置,因为后面snlua就打酱油了,再也回不到它的领空了。

至此,一个lua服务就得以运行,入口脚本只需要利用skynet的lua api注册一次回调函数,那么就可以接管消息的处理了。


skynet的lua封装

这个封装的是c层的核心接口,实现在/lualib-src/lua_skynet.c中:

 1 int
 2 luaopen_skynet_core(lua_State *L) {
 3     luaL_checkversion(L);
 4
 5     luaL_Reg l[] = {
 6         { "send" , lsend },
 7         { "genid", lgenid },
 8         { "redirect", lredirect },
 9         { "command" , lcommand },
10         { "intcommand", lintcommand },
11         { "error", lerror },
12         { "tostring", ltostring },
13         { "harbor", lharbor },
14         { "pack", luaseri_pack },
15         { "unpack", luaseri_unpack },
16         { "packstring", lpackstring },
17         { "trash" , ltrash },
18         { "callback", lcallback },
19         { "now", lnow },
20         { NULL, NULL },
21     };
22
23     luaL_newlibtable(L, l);
24
25     lua_getfield(L, LUA_REGISTRYINDEX, "skynet_context");
26     struct skynet_context *ctx = lua_touserdata(L,-1);
27     if (ctx == NULL) {
28         return luaL_error(L, "Init skynet context first");
29     }
30
31     luaL_setfuncs(L,l,1);
32
33     return 1;
34 }

接口很少,因为c层的接口很少,就不一一叙述了。


至此,两个必要条件满足,lua binding的核心就完成了,剩下的就是如何让业务层使用起来更方便的封装了,主要针对消息分发,这也是skynet最为复杂的地方,下一篇再讲。

转载于:https://www.cnblogs.com/watercoldyi/p/5910704.html

skynet源码分析5:lua绑定之地基相关推荐

  1. skynet源码分析之lua层消息处理

    Lua层消息处理机制在lualib/skynet.lua,提供大部分Lua层的api(最终会调用到c层的api),包括启动一个snlua服务时Lua层的处理,创建新服务,注册服务协议,如何发送消息,如 ...

  2. skynet源码分析 make

    文章目录 skynet源码分析 make 前言 正文 platform.mk Makefile jemalloc skynet 附录 skynet源码分析 make 前言 本文的版本选择的是skyne ...

  3. skynet源码分析之定时器skynet_timer.c

    skynet自带定时器功能skynet-src/skynet_timer.c,在skynet启动时会创建一个线程专门跑定时器.每帧(0.0025秒/帧)调用skynet_updatetime() 1 ...

  4. skynet源码分析:服务,Actor模型,lua接口编程,demo演示Actor编程思维

    skynet刚开始是单进程多线程的,它是由一个一个的服务组成的.在skynet上做开发,实际上就是在写服务.服务与服务之间通过消息队列进行通信. 做为核心功能,Skynet 仅解决一个问题: 把一个符 ...

  5. skynet源码分析(11)--skynet的配置加载

    作者:shihuaping0918@163.com,转载请注明作者 skynet中的源码已经分析得差不多了,还有启动过程没有分析.skynet的配置文件是以lua格式来写的.使用过skynet的都清楚 ...

  6. skynet 框架snax源码分析----变量注入

    skynet为了简化服务的编写,推出了snax框架,源码里也有一个例子pingserver.这是snax原创文章的第一篇,所以先就分析snax框架里的interface.lua源码,它的实现应用了一个 ...

  7. asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证

    asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证 原文:asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型 ...

  8. jQuery源码分析-10事件处理-Event-事件绑定与删除-bind/unbind+live/die+delegat/unde

    10.4    .bind() .one() 10.4.1  如何使用 .bind( eventType, [eventData], handler(eventObject) )   在匹配的元素上绑 ...

  9. jQuery源码分析-10事件处理-Event-事件绑定与删除-bind/unbind+live/die+delegat/undelegate

    Js代码   作者:nuysoft/高云 QQ:47214707 EMail:nuysoft@gmail.com 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. 后文预告:封装事件对象 便 ...

最新文章

  1. oracle与mysql创建表时的区别
  2. 计算机组成原理 — CPU — 缓存访问
  3. ESP32­-PICO-­D4的使用
  4. unity android 启动,Android启动Unity
  5. 程序员的创业陷阱:接私活
  6. 中国产教融合市场发展模式与运营前景咨询报告2022版
  7. 【CentOS Linux 7】实验1【Linux文件目录管理】
  8. LL-verilog语法:case用法
  9. ibatis mysql 自增_mybatis自增主键
  10. excel插入行 uipath_Uipath中excel的activities用法介绍
  11. 君威u0073故障码解决_格力多联机同时报三个故障“U2”“L0”“d9”怎么修?
  12. Java 面试如何坐等 offer?
  13. iOS开发类似于刮刮卡效果,手指划过的区域形成画笔。适用于取出部分图片(截图),如截取出图片中带文字的区域部分。...
  14. 网页设计与制作 项目教程 项目1
  15. unity WebGL射线检测
  16. 字符串函数的使用及模拟实现:strcpy、strcmp、strcat、strstr
  17. Python——提取复数类型的数组的的实数部分和虚数部分
  18. 基于Debezium 1.6和Oracle 11g 的 Debezium-Oracle实战
  19. 计算机运行库,VC2010运行库
  20. tkinter -- tcp

热门文章

  1. mysql百万级数据测试_百万级数据mysql测试环境介绍
  2. C# pdf 转图片 and 创建百度AI文字识别应用(识别图片中的文字和数字)
  3. src refspec xxx does not match any
  4. SVN更新有问题 svn The working copy at‘ ‘ is too old
  5. OpenWHO课程 Introduction to COVID-19: methods for detection, prevention, response and control 小抄
  6. 【CCCC】L3-003 社交集群 (30分),并查集模板,map排序
  7. java 线程不安全例子_Java中多线程安全问题实例分析
  8. jQuery→事件、jQuery事件对象属性方法、多事件、自定义事件
  9. 计算机网络交换机组网及虚拟局域网实验报告,计算机网络实验报告材料(虚拟局域网).doc...
  10. ubuntu装python3_ubuntu16.04安装python3的包报错