• skynet 框架snax源码分析1---变量注入


    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" }
    
        do
            for k, v in ipairs(system) do
                system[v] = k
                func[k] = { k , "system", v }
            end
        end

    首先看上面这断代码,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 + 1
        print (i, hello)
        end)
    end
    
    function response.ping(hello)
        skynet.sleep(100)
        return hello
    end

    只有在snax框架里,上述代码才会工作,否则skynet框架会报错,因为找不不response与accetp对象。那变量是怎么注入的呢

       do
            local path = skynet.getenv "snax"
    
            local errlist = {}
    
            for pat in string.gmatch(path,"[^;]+") do
                local filename = string.gsub(pat, "?", name)
                local f , err = loader(filename, "bt", G)
                if f then
                    pattern = pat
                    mainfunc = f
                    break
                else
                    table.insert(errlist, err)
                end
            end
    
            if mainfunc == nil then
                error(table.concat(errlist, "
    "))
            end
        end
    
        mainfunc()

    上面这段代码,首先从配置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"

    测试结果:

    image

    最后,如果你不想去下源码,只想马上尝试一下,点击下面的地址,即可在CloudfusionIDE里在线阅读源码,甚至运行(Google/火狐浏览器):http://cloudfusion.cc/ide/cloudfusion/skynet

    转载请注明出处

  • 相关阅读:
    【洛谷P6139】【模板】广义后缀自动机(广义 SAM)
    【CF700E】Cool Slogans
    【CF605E】Intergalaxy Trips
    【洛谷P3638】机器人
    【洛谷P4762】Virus synthesis
    Web前端从入门到精通-8 css简介——清除浮动
    Web前端从入门到精通-7 css简介——浮动
    Web前端从入门到精通-6 css简介——选择器优先级
    Web前端从入门到精通-5 css简介——css概述和选择器
    Web前端从入门到精通-4 html简介
  • 原文地址:https://www.cnblogs.com/BlankEye/p/4116374.html
Copyright © 2020-2023  润新知