From df5c619a29c2d89adf7bffeb18eacb3d156539fb Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Wed, 16 Apr 2014 20:12:17 +0800 Subject: [PATCH] snax : a simple skynet auxliliary framework --- examples/config | 1 + lualib/snax.lua | 98 +++++++++++++++++++++++++++++++++++++++ lualib/snax_interface.lua | 84 +++++++++++++++++++++++++++++++++ service/snaxd.lua | 33 +++++++++++++ test/pingserver.lua | 28 +++++------ test/testping.lua | 13 +++--- 6 files changed, 235 insertions(+), 22 deletions(-) create mode 100644 lualib/snax.lua create mode 100644 lualib/snax_interface.lua create mode 100644 service/snaxd.lua diff --git a/examples/config b/examples/config index 4243a214..a6d9f5ad 100644 --- a/examples/config +++ b/examples/config @@ -7,5 +7,6 @@ master = "127.0.0.1:2013" start = "main" standalone = "0.0.0.0:2013" luaservice = root.."service/?.lua;"..root.."test/?.lua;"..root.."examples/?.lua" +snax = root.."examples/?.lua;"..root.."test/?.lua" cpath = root.."cservice/?.so" redis = root .. "examples/redisconf" diff --git a/lualib/snax.lua b/lualib/snax.lua new file mode 100644 index 00000000..cca3032e --- /dev/null +++ b/lualib/snax.lua @@ -0,0 +1,98 @@ +local skynet = require "skynet" +local snax_interface = require "snax_interface" + +local snax = {} +local typeclass = {} + +local G = { require = function() end } + +function snax.interface(name) + if typeclass[name] then + return typeclass[name] + end + + local si = snax_interface(name, G) + + local ret = { + subscribe = {}, + response = {}, + system = {}, + } + + for _,v in ipairs(si) do + local id, group, name, f = table.unpack(v) + if group == "system" then + if f then + ret.system[name] = id + end + else + ret[group][name] = id + end + end + + typeclass[name] = ret + return ret +end + +local meta = { __tostring = function(v) return string.format("[%s:%x]", v.type, v.handle) end} + +local skynet_send = skynet.send +local skynet_call = skynet.call + +local function gen_pub(type, handle) + return setmetatable({} , { + __index = function( t, k ) + local id = assert(type.subscribe[k] , string.format("publish %s no exist", k)) + return function(...) + skynet_send(handle, "lua", id, ...) + end + end }) +end + +local function gen_req(type, handle) + return setmetatable({} , { + __index = function( t, k ) + local id = assert(type.response[k] , string.format("request %s no exist", k)) + return function(...) + return skynet_call(handle, "lua", id, ...) + end + end }) +end + +local function wrapper(handle, name, type) + return setmetatable ({ + pub = gen_pub(type, handle), + req = gen_req(type, handle), + type = name, + handle = handle, + kill = function(...) return skynet_call(handle, "lua", type.system.exit, ...) end, + }, meta) +end + +local handle_cache = setmetatable( {} , { __mode = "kv" } ) + +function snax.newservice(name, ...) + local t = snax.interface(name) + local handle = skynet.newservice("snaxd", name) + assert(handle_cache[handle] == nil) + if t.system.init then + skynet.call(handle, "lua", t.system.init, ...) + end + local ret = wrapper(handle, name, t) + handle_cache[handle] = ret + return ret +end + +function snax.bind(handle, type) + local ret = handle_cache[handle] + if ret then + assert(ret.type == type) + return ret + end + local t = snax.interface(type) + ret = wrapper(handle, type, t) + handle_cache[handle] = ret + return ret +end + +return snax diff --git a/lualib/snax_interface.lua b/lualib/snax_interface.lua new file mode 100644 index 00000000..7f4c24dc --- /dev/null +++ b/lualib/snax_interface.lua @@ -0,0 +1,84 @@ +local skynet = require "skynet" + +return function (name , G) + local mainfunc + + local function func_id(id, group) + local tmp = {} + local function count( _, name, func) + if type(name) ~= "string" then + error (string.format("%s method only support string", group)) + end + if type(func) ~= "function" then + error (string.format("%s.%s must be function"), group, name) + end + if tmp[name] then + error (string.format("%s.%s duplicate definition", group, name)) + end + tmp[name] = true + table.insert(id, { #id + 1, group, name, func} ) + end + return setmetatable({}, { __newindex = count }) + end + + do + assert(getmetatable(G) == nil) + assert(G.init == nil) + assert(G.exit == nil) + + assert(G.subscribe == nil) + assert(G.response == nil) + end + + local env = {} + local func = {} + + local system = { "init", "exit" } + + do + for k, v in ipairs(system) do + system[v] = k + func[k] = { k , "system", v } + end + end + + env.subscribe = func_id(func, "subscribe") + env.response = func_id(func, "response") + + local function init_system(t, name, f) + local index = assert(system[name] , string.format("Not support global var %s", name)) + if type(f) ~= "function" then + error (string.format("%s must be a function", name)) + end + func[index][4] = f + end + + setmetatable(G, { __index = env , __newindex = init_system }) + + do + local path = skynet.getenv "snax" + + local errlist = {} + + for pat in string.gmatch(path,"[^;]+") do + local filename = string.gsub(pat, "?", name) + local f , err = loadfile(filename, "bt", G) + if f then + mainfunc = f + break + else + table.insert(errlist, err) + end + end + + if mainfunc == nil then + error(table.concat(errlist, "\n")) + end + end + + mainfunc() + + setmetatable(G, nil) + + return func +end diff --git a/service/snaxd.lua b/service/snaxd.lua new file mode 100644 index 00000000..6bd68086 --- /dev/null +++ b/service/snaxd.lua @@ -0,0 +1,33 @@ +local skynet = require "skynet" +local snax_interface = require "snax_interface" + +local func = snax_interface(tostring(...), _ENV) + +skynet.start(function() + local init = false + skynet.dispatch("lua", function ( _, _, id, ...) + local method = func[id] + if method[2] == "system" then + if method[3] == "init" then + assert(not init, "Already init") + local initfunc = method[4] or function() end + skynet.ret(skynet.pack(initfunc(...))) + init = true + else + assert(init, "Never init") + local exitfunc = method[4] or function() end + skynet.ret(skynet.pack(exitfunc(...))) + init = false + skynet.exit() + end + else + assert(init, "Init first") + if method[2] == "subscribe" then + -- no return + method[4](...) + else + skynet.ret(skynet.pack(method[4](...))) + end + end + end) +end) diff --git a/test/pingserver.lua b/test/pingserver.lua index 49496608..aeeda6cb 100644 --- a/test/pingserver.lua +++ b/test/pingserver.lua @@ -1,27 +1,23 @@ local skynet = require "skynet" -local command = {} - -function command.PING(hello) +function response.ping(hello) + skynet.sleep(100) return hello end -function command.HELLO() - skynet.sleep(100) - return "hello" +function subscribe.hello() + print "hello" end -function command.EXIT() - skynet.exit() -end - -function command.ERROR() +function response.error() error "throw an error" end +function init( ... ) + print ("ping server start:", ...) +end -skynet.start(function() - skynet.dispatch("lua", function(session,addr, cmd, ...) - skynet.ret(skynet.pack(command[cmd](...))) - end) -end) +function exit(...) + print ("ping server exit:", ...) + return "Exit" +end diff --git a/test/testping.lua b/test/testping.lua index 59a2ad5d..9a141b43 100644 --- a/test/testping.lua +++ b/test/testping.lua @@ -1,10 +1,11 @@ local skynet = require "skynet" +local snax = require "snax" skynet.start(function() - local ps = skynet.uniqueservice("pingserver") - skynet.watch(ps) - print(pcall(skynet.call,ps,"lua","ERROR")) - print(skynet.call(ps, "lua", "PING", "hello")) - print(skynet.call(ps, "lua", "PING", "hay")) - skynet.call(ps, "lua", "EXIT") + local ps = snax.newservice ("pingserver", "hello world") + print(ps.pub.hello()) + print(ps.req.ping("foobar")) + print(pcall(ps.req.error)) + print(ps.kill("exit")) + skynet.exit() end)