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, "\n"))
        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

轉載請注明出處


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM