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源码分析----变量注入相关推荐

  1. 视频教程-RPC服务框架(Dubbo)源码分析-Java

    RPC服务框架(Dubbo)源码分析 鲁班学院-子路老师曾就职于谷歌.天猫电商等多家互联网公司,历任java架构师.研发经理等职位,参与并主导千万级并发电商网站与后端供应链研发体系搭建,多次参与电商大 ...

  2. Apollo 2.0 框架及源码分析(一) | 软硬件框架

    原文地址:https://zhuanlan.zhihu.com/p/33059132 前言 如引言中介绍的,这篇软硬件框架多为现有消息的整合加一些个人的想法.关于 Apollo 介绍的文章已经有许多, ...

  3. Python微型Web框架Bottle源码分析

    Bottle 是一个快速,简单和轻量级的 WSGI 微型 Web 框架的 Python.它作为单个文件模块分发,除了 Python 标准库之外没有依赖关系. 选择源码分析的版本是 Release 于 ...

  4. Spring框架—SpringBean源码分析

    原文作者:Javadoop 原文地址:Spring IOC 容器源码分析 在继续往下之前,我们需要先了解 BeanDefinition.我们说 BeanFactory 是 Bean 容器,那么 Bea ...

  5. 高性能网络I/O框架-netmap源码分析

    前几天听一个朋友提到这个netmap,看了它的介绍和设计,确实是个好东西.其设计思想与业界不谋而合--因为为了提高性能,几个性能瓶颈放在那里,解决方法自然也是类似的. netmap的出现,它既实现了一 ...

  6. FATFS文件系统框架及源码分析

    FATFS是一个为小型嵌入式系统设计的通用FAT(File Allocation Table)文件系统模块.FatFs 的编写遵循ANSI C,并且完全与磁盘I/O层分开.因此,它独立(不依赖)于硬件 ...

  7. 阿里开源一站式分布式事务框架seata源码分析(AT模式下TM与RM分析)

    序言: 对于阿里开源分布式事务框架seata的详细了解可以参考官网,这里不会详细介绍.本章只会介绍seata中AT模式的源码分析(对阿seata有一定了解或者成功完成过demo). seata中一个事 ...

  8. Java类集框架 —— HashMap源码分析

    HashMap是基于Map的键值对映射表,底层是通过数组.链表.红黑树(JDK1.8加入)来实现的. HashMap结构 HashMap中存储元素,是将key和value封装成了一个Node,先以一个 ...

  9. Java类集框架 —— LinkedList源码分析

    在JDK1.7之前,LinkedList是采用双向环形链表来实现的,在1.7及之后,Oracle将LinkedList做了优化,将环形链表改成了线性链表.本文对于LinkedList的源码分析基于JD ...

最新文章

  1. DB天气app冲刺二阶段第十一天(完结)
  2. 什么样的程序员生涯指南,能在GitHub上获3.6万星
  3. js == 和 ===
  4. 转 知道这20个正则表达式,能让你少写1,000行代码
  5. 云炬随笔20180421
  6. 设计模式之_工厂系列_03
  7. junit5和junit4_JUnit声明异常– JUnit 5和JUnit 4
  8. java基础_day02
  9. python程序如何发布
  10. usb书:圈圈教你玩USB
  11. 腾讯微博开放平台API SDK vb版源码发布
  12. 分时操作系统与分布式操作系统
  13. win7电脑屏幕亮度怎么调节
  14. wav格式怎么转换mp3?
  15. 明日方舟如何在电脑上玩 明日方舟模拟器教程
  16. Meltdown Reading Kernel Memory from User Space
  17. coldfusion php,将一些PHP移植到ColdFusion
  18. 【SNA】社会网络分析二 Gephi 功能详解
  19. librtmp推流到YouTube失败
  20. 能够正常加入域但无法实施域策略

热门文章

  1. 美国华人一个半世纪的沧桑
  2. 雷军为啥那么煽情:回到初心,芝麻开门
  3. 一个不错的分享源码的网站
  4. windows下的Nmap的下载安装和使用
  5. 导致论文高被引的关键因素
  6. 腾讯电脑管家网址认证服务 定位行业平台
  7. 程序员真的可以轻松来一套苹果全家桶吗
  8. rpt层构建以及实现,220626,hm
  9. 像外行一样思考 像专家一样实践
  10. Android 快速接入腾讯云人脸核身(识别)