skynet 框架snax源码分析----变量注入
skynet为了简化服务的编写,推出了snax框架,源码里也有一个例子pingserver。这是snax原创文章的第一篇,所以先就分析snax框架里的interface.lua源码,它的实现应用了一个闭包中的upvalue注入技巧。
凡是框架都得遵循框架的约定,snax有两个大的约定,一是约定了一组预置的接口init/exit/hotfix;二是accept/response这两组用来编写服务的接口。本文,并不涉及这些,而是谈accept/response是如何注入给snax服务的。
snax框架里new一个服务的流程如下,最终调用到snax_inteface里的interface.lua,该文件里只有一个函数:
snax.newservice->snax.rawnewservice->snax.interface->snax_interface
local temp_global = {}local env = setmetatable({} , { __index = temp_global })local func = {}local system = { "init", "exit", "hotfix" }dofor k, v in ipairs(system) dosystem[v] = kfunc[k] = { k , "system", v }endend
首先看上面这断代码,func是函数最终要返回的对象,它 是一个表。在do语句块里,func最终会被扩展为添加了三个system接口的数组。接下来就是两行代码,
env.accept = func_id(func, "accept")
env.response = func_id(func, "response")
func_id再做什么呢,func_id返回的是一个空表,这个表上设置了一个元表,并重写了元表的__newindex,也就是对该表新键赋值时会触发的操作,env.accept/env.response都是一个带元表的空表,这里也就是用户编写snax服务需要用到的两个表,看一下pingserver.lua
function accept.hello()lock(function()i = i + 1print (i, hello)end)
endfunction response.ping(hello)skynet.sleep(100)return hello
end
只有在snax框架里,上述代码才会工作,否则skynet框架会报错,因为找不不response与accetp对象。那变量是怎么注入的呢
dolocal path = skynet.getenv "snax"local errlist = {}for pat in string.gmatch(path,"[^;]+") dolocal filename = string.gsub(pat, "?", name)local f , err = loader(filename, "bt", G)if f thenpattern = patmainfunc = fbreakelsetable.insert(errlist, err)endendif mainfunc == nil thenerror(table.concat(errlist, "\n"))endendmainfunc()
上面这段代码,首先从配置snax路径里去查找到指定的编写的snax服务,找到之后,就用loader去加载文件,这个loader默认情况下是是lua API loadfile
loader = loader or loadfile
变量注入依靠的就是这个loadfile,其实呢,我也是从这份代码里首次看到loadfile这样使用,谢谢云风。一般就只用到第一个参数,第二个参数都没看到用。
看一下loadfile的C实现
static int luaB_loadfile (lua_State *L) {const char *fname = luaL_optstring(L, 1, NULL);const char *mode = luaL_optstring(L, 2, NULL);int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */int status = luaL_loadfilex(L, fname, mode);return load_aux(L, status, env);
}static int load_aux (lua_State *L, int status, int envidx) {if (status == LUA_OK) {if (envidx != 0) { /* 'env' parameter? */lua_pushvalue(L, envidx); /* environment for loaded function */if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */lua_pop(L, 1); /* remove 'env' if not used by previous call */}return 1;}else { /* error (message is on top of the stack) */lua_pushnil(L);lua_insert(L, -2); /* put before error message */return 2; /* return nil plus error message */}
}
首先,luaB_loadfile会看一下env是否设置,然后调用load_aux,这里如果loadfile的第三个参数正确设置就会调用lua_setupvalue,把env设置为upvalue。
当成功loadfile之后的mainfunc,会立即执行,这时pingserver就能正确找到变量upvalue中的response与accept
最后,skynet源码里没有启动pingserver的配置文件,下面给一个:
config_snax
root = "./"
thread = 8
logger = nil
logpath = "."
harbor = 1
address = "127.0.0.1:2526"
master = "127.0.0.1:2013"
start = "simplesnax" -- main script
bootstrap = "snlua bootstrap" -- The service for bootstrap
standalone = "0.0.0.0:2013"
luaservice = root.."service/?.lua;"..root.."test/?.lua;"..root.."examples/?.lua"
lualoader = "lualib/loader.lua"
-- preload = "./examples/preload.lua" -- run preload.lua before every lua service run
snax = root.."examples/?.lua;"..root.."test/?.lua"
cpath = root.."cservice/?.so"
-- daemon = "./skynet.pid"
start的simplesnax:
local skynet = require "skynet"
local snax = require "snax"skynet.start(function()local ps = snax.newservice("pingserver", "hello world")
end)
最后测试文件:config_ps
thread = 8
mqueue = 256
cpath = "./cservice/?.so"
logger = nil
harbor = 2
address = "127.0.0.1:2527"
master = "127.0.0.1:2013"
start = "testping"
luaservice ="./service/?.lua;./test/?.lua;./examples/?.lua"
snax = "./examples/?.lua;./test/?.lua"
推荐自己的技术交流群:【960994558】整理了一些个人觉得比较好的学习书籍、大厂面试题、有趣的项目和热门技术教学视频资料共享在里面(包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等等.),有需要的可以自行添加哦!~
最后邀请大家参与一个skynet的分享直播:
大佬带你四小时玩转skynet-------------------
skynet 框架snax源码分析----变量注入相关推荐
- 视频教程-RPC服务框架(Dubbo)源码分析-Java
RPC服务框架(Dubbo)源码分析 鲁班学院-子路老师曾就职于谷歌.天猫电商等多家互联网公司,历任java架构师.研发经理等职位,参与并主导千万级并发电商网站与后端供应链研发体系搭建,多次参与电商大 ...
- Apollo 2.0 框架及源码分析(一) | 软硬件框架
原文地址:https://zhuanlan.zhihu.com/p/33059132 前言 如引言中介绍的,这篇软硬件框架多为现有消息的整合加一些个人的想法.关于 Apollo 介绍的文章已经有许多, ...
- Python微型Web框架Bottle源码分析
Bottle 是一个快速,简单和轻量级的 WSGI 微型 Web 框架的 Python.它作为单个文件模块分发,除了 Python 标准库之外没有依赖关系. 选择源码分析的版本是 Release 于 ...
- Spring框架—SpringBean源码分析
原文作者:Javadoop 原文地址:Spring IOC 容器源码分析 在继续往下之前,我们需要先了解 BeanDefinition.我们说 BeanFactory 是 Bean 容器,那么 Bea ...
- 高性能网络I/O框架-netmap源码分析
前几天听一个朋友提到这个netmap,看了它的介绍和设计,确实是个好东西.其设计思想与业界不谋而合--因为为了提高性能,几个性能瓶颈放在那里,解决方法自然也是类似的. netmap的出现,它既实现了一 ...
- FATFS文件系统框架及源码分析
FATFS是一个为小型嵌入式系统设计的通用FAT(File Allocation Table)文件系统模块.FatFs 的编写遵循ANSI C,并且完全与磁盘I/O层分开.因此,它独立(不依赖)于硬件 ...
- 阿里开源一站式分布式事务框架seata源码分析(AT模式下TM与RM分析)
序言: 对于阿里开源分布式事务框架seata的详细了解可以参考官网,这里不会详细介绍.本章只会介绍seata中AT模式的源码分析(对阿seata有一定了解或者成功完成过demo). seata中一个事 ...
- Java类集框架 —— HashMap源码分析
HashMap是基于Map的键值对映射表,底层是通过数组.链表.红黑树(JDK1.8加入)来实现的. HashMap结构 HashMap中存储元素,是将key和value封装成了一个Node,先以一个 ...
- Java类集框架 —— LinkedList源码分析
在JDK1.7之前,LinkedList是采用双向环形链表来实现的,在1.7及之后,Oracle将LinkedList做了优化,将环形链表改成了线性链表.本文对于LinkedList的源码分析基于JD ...
最新文章
- DB天气app冲刺二阶段第十一天(完结)
- 什么样的程序员生涯指南,能在GitHub上获3.6万星
- js == 和 ===
- 转 知道这20个正则表达式,能让你少写1,000行代码
- 云炬随笔20180421
- 设计模式之_工厂系列_03
- junit5和junit4_JUnit声明异常– JUnit 5和JUnit 4
- java基础_day02
- python程序如何发布
- usb书:圈圈教你玩USB
- 腾讯微博开放平台API SDK vb版源码发布
- 分时操作系统与分布式操作系统
- win7电脑屏幕亮度怎么调节
- wav格式怎么转换mp3?
- 明日方舟如何在电脑上玩 明日方舟模拟器教程
- Meltdown Reading Kernel Memory from User Space
- coldfusion php,将一些PHP移植到ColdFusion
- 【SNA】社会网络分析二 Gephi 功能详解
- librtmp推流到YouTube失败
- 能够正常加入域但无法实施域策略