精华内容
下载资源
问答
  • skynet框架应用——mongodb

    在skynet中使用mongodb,在
    在配置文件config.mongodb中看到启动的是main_mongodb.lua
    main_mongodb.lua文件内容

    local skynet = require "skynet"
    
    skynet.start(function()
    	print("Main Server start")
    	local console = skynet.newservice(
    		"testmongodb", "127.0.0.1", 27017, "testdb", "test", "test"
    	)
    	
    	print("Main Server exit")
    	skynet.exit()
    end)
    

    testmongodb.lua文件在官方的示例中有,大家可以自己去看,我们这里不展开。
    接下来要说的是,在skynet中,我们怎么更方便地使用mongodb。
    我们考虑的是将mongodb单独成一个服务,
    在使用时候,用skynet.call去调用它,示例:

    local mongodb = skynet.newservice("mongodb")
    skynet.call(mongodb, "lua", "connect", {
           host = "127.0.0.1",
           port = 27017
    })
    

    第一个文件:
    mongodb.lua

    local skynet = require "skynet"
    require "skynet.manager"
    local dbLogic = require "dbLogic"
    
    skynet.start(function()
    	skynet.dispatch("lua", function(session, address, cmd, ...)
    		local f = dbLogic[cmd]
    		if f then
    			skynet.ret(skynet.pack(f(...)))
    		else
    			error(string.format("Unknown command %s", tostring(cmd)))
    		end
    	end)
    
    	skynet.register ".mongodb"
    end)
    

    我们把对mongodb的增、删、查、改都放到了dbLogic.lua文件中:

    local skynet = require "skynet"
    require "skynet.manager"
    local mongo = require "skynet.db.mongo"
    
    local client
    
    local _M = {}
    
    function _M.connect(args)
    	client = mongo.client(
    		{
    			host = args.host, 
                port = tonumber(args.port)
    		}
    	)
    
        if client then
            return true
        end
        return false
    end
    
    function _M.disconnect()
        client:disconnect()
    end
    
    function _M.insert(args)
        local db = client:getDB(args.database)
        local c = db:getCollection(args.collection)
        c:insert(args.doc)
    end
    
    function _M.insertBatch(args)
    	local db = client:getDB(args.database)
    	local c = db:getCollection(args.collection)
    	c:batch_insert(args.docs)
    end
    
    function _M.delete(args)
    	local db = client:getDB(args.database)
    	local c = db:getCollection(args.collection)
    	c:delete(args.selector, args.single)
    end
    
    function _M.drop(args)
    	local db = client:getDB(args.database)
    	local r = db:runCommand("drop", args.collection)
    	return r
    end
    
    function _M.findOne(args)
    	local db = client:getDB(args.database)
    	local c = db:getCollection(args.collection)
        local result = c:findOne(args.query, args.selector)
    	return result
    end
    
    function _M.findAll(args)
    	local db = client:getDB(args.database)
    	local c = db:getCollection(args.collection)
    	local result = {}
    	local cursor = c:find(args.query, args.selector)
    	if args.skip ~= nil then
    		cursor:skip(args.skip)
    	end
    	if args.limit ~= nil then
    		cursor:limit(args.limit)
    	end
    	while cursor:hasNext() do
    		local document = cursor:next()
    		table.insert(result, document)
    	end	
        cursor:close()
    
        if #result > 0 then
            return result
        end
    end
    
    function _M.update(args)
    	local db = client:getDB(args.database)
    	local c = db:getCollection(args.collection)
    	c:update(args.selector, args.update, args.upsert, args.multi)
    end
    
    function _M.createIndex(args)
    	local db = client:getDB(args.database)
    	local c = db:getCollection(args.collection)
    	local result = c:createIndex(args.keys, args.option)
    	return result
    end
    
    function _M.runCommand(args)
    	local db = client:getDB(args.database)
    	local result = db:runCommand(args)
    	return result
    end
    
    return _M
    

    为了方便查看table中的数据,我们将util.lua拷贝过来,放到和main.lua同一个文件下。
    具体内容查看这篇博客:https://blog.csdn.net/hp_cpp/article/details/104882400

    现在我们开始对dbLogic.lua的接口进行测试和说明:

    一、连接数据库

    local skynet = require "skynet"
    local util = require "util"
    
    skynet.start(function()
    	print("Main Server start")
    	local mongodb = skynet.newservice("mongodb")
    	
        local ret = skynet.call(mongodb, "lua", "connect", {
            host = "127.0.0.1",
            port = 27017
        })
        print("connect ret:", ret)
    	--以下测试均在此处添加代码
    	
    	skynet.exit()
    end)
    

    打印出true,表示连接成功。
    我们现在用的mongo数据库在默认端口,没有用户名和密码的,而连接数据库也很简单,只需要提供两个参数host和port:

    client = mongo.client({host = args.host, 
    					   port = tonumber(args.port)})
    

    在这里插入图片描述我们手动建一个testdb数据库,以下操作均在testdb数据中进行。

    二、插入

    (1)单条插入

    	skynet.call(mongodb, "lua", "insert", {
    		database = "testdb",
    		collection = "role",
    		doc = {test = "haha", num = 100}
    	})
    

    这个操作在dbLogic.lua文件没有定义返回值,所以我们去查看数据库,果然新增了一个role表和插入一条记录
    在这里插入图片描述
    (1)批量插入

        skynet.call(mongodb, "lua", "insertBatch", {
            database = "testdb",
            collection = "role",
            docs = {
                {test = "hi", num = 101},
                {test = "me to", num = 102}}
        })
    

    此时role表中又多了几条记录。

    三、删除

    (1)删除记录

        skynet.call(mongodb, "lua", "delete", {
    		database = "testdb",
            collection = "role",
            selector = {num=100},
            single = 1,
        })
    

    这里删除了role表中所有num=100的记录,(single为整形,表示删除多少条记录,或者说是删除多少个doc),具体可以查看以下两个文件:
    在skynet-master\lualib\skynet\db\mongo.lua文件中:

    function mongo_collection:delete(selector, single)
    	local sock = self.connection.__sock
    	local pack = driver.delete(self.full_name, single, bson_encode(selector))
    	sock:request(pack)
    end
    

    在去查skynet-master\lualib-src\lua-mongo.c文件:

    /*
    	1 string collection
    	2 integer single remove
    	3 document selector
    
    	return string package
     */
    static int
    op_delete(lua_State *L) {
    	document selector  = lua_touserdata(L,3);
    	if (selector == NULL) {
    		luaL_error(L, "Invalid param");
    	}
    	size_t sz = 0;
    	const char * name = luaL_checklstring(L,1,&sz);
    
    	luaL_Buffer b;
    	luaL_buffinit(L,&b);
    
    	struct buffer buf;
    	buffer_create(&buf);
    		int len = reserve_length(&buf);
    		write_int32(&buf, 0);
    		write_int32(&buf, 0);
    		write_int32(&buf, OP_DELETE);
    		write_int32(&buf, 0);
    		write_string(&buf, name, sz);
    		write_int32(&buf, lua_tointeger(L,2));
    
    		int32_t selector_len = get_length(selector);
    		int total = buf.size + selector_len;
    		write_length(&buf, total, len);
    
    		luaL_addlstring(&b, (const char *)buf.ptr, buf.size);
    	buffer_destroy(&buf);
    
    	luaL_addlstring(&b, (const char *)selector, selector_len);
    	luaL_pushresult(&b);
    
    	return 1;
    }
    

    (2)清空表

        ret = skynet.call(mongodb, "lua", "drop", {
    		database = "testdb",
            collection = "role",
        })
        
        util.dump_table(ret)
    

    输出结果:

        S:nIndexesWas -> N:1
        S:ok -> N:1.0
        S:ns -> S:testdb.role
    

    此时看到
    在这里插入图片描述
    role中的记录全部删除
    返回的是一个table,我们用util模块中的util.dump_table打印出来,看到有个ok = 1.0,通过比较这个ok是否等于1,我们可以知道该操作是否成功
    如果我们再执行一遍清空表操作:
    则运行结果:

        S:errmsg -> S:ns not found
        S:ok -> N:0.0
        S:code -> N:26
        S:codeName -> S:NamespaceNotFound
    

    提示我们操作错误了。

    四、查找

    (1)查找单条记录

    	ret = skynet.call(mongodb, "lua", "findOne", {
    		database = "testdb",
    		collection = "role",
    		query = {num = 100},
    		selector = {test=1}
        })
        
        util.dump_table(ret)
    

    当找到该文档时,输出:

        S:_id -> S:^n��(�G�g�
        S:test -> S:haha
    

    没找到时,返回nil
    这里的参数要说明一下:
    query是查询条件,{num = 100},这里可以写多个条件。
    selector是刷选器,也就是最后结果中需要的字段(除_id外,其余字段),比如我们这里只填入了一个test=1,这里的筛选的字段要作为key,如果是空表,则表示全部字段都要。

    	ret = skynet.call(mongodb, "lua", "findOne", {
    		database = "testdb",
    		collection = "role",
    		query = {num = 100},
    		selector = {}
        })
    

    运行结果:

        S:test -> S:haha
        S:num -> N:100
        S:_id -> S:^n��(��wf�
    

    (1)查找全部

    	ret = skynet.call(mongodb, "lua", "findAll", {
    		database = "testdb",
    		collection = "role",
    		query = {num = 100},
    		selector = {test=1}
    	})
        util.dump_table(ret)
    

    参数说明同上,只是这里的返回值略有不同,返回多条记录。(比如role表中有3条记录)

        N:1 -> 
            S:_id -> S:^n��(��wf�
            S:test -> S:haha
        N:2 -> 
            S:_id -> S:^n��(��7f�
            S:test -> S:haha
        N:3 -> 
            S:_id -> S:^nF�(���
            S:test -> S:haha
    

    五、修改

        local updater = {}
        updater["$set"] = {test = "hello world1"}			
        
        local allResult = skynet.call(mongodb, "lua", "update", {
            database = "testdb",
            collection = "role",
            selector = {num=100},
            update = updater,
            upsert = false,
            multi = false
        })
        util.dump_table(ret)
    

    参数说明:
    updater用来设置更新字段和内容
    这里updater["$set"]是固定格式,后面是一个表,记录需要修改的字段和内容。

    updater["$set"] = {test = "hello world1"}	
    

    upsert 布尔值,true表示不存在则新增一条新文档,false表示不存在则不做任何操作

    multi 布尔值,true表表示多行更新,flase表示只会更新符合条件的一条记录

    main.lua中的全部代码:

    local skynet = require "skynet"
    local util = require "util"
    
    skynet.start(function()
    	print("Main Server start")
    	local mongodb = skynet.newservice("mongodb")
    
        local ret = skynet.call(mongodb, "lua", "connect", {
            host = "127.0.0.1",
            port = 27017
        })
        print("connect ret:", ret)
    	
    	skynet.call(mongodb, "lua", "insert", {
    		database = "testdb",
    		collection = "role",
    		doc = {test = "haha", num = 100}
    	})
        
        -- skynet.call(mongodb, "lua", "insertBatch", {
        --     database = "testdb",
        --     collection = "role",
        --     docs = {
        --         {test = "hi", num = 101},
        --         {test = "me to", num = 102}}
        -- })
        
        -- skynet.call(mongodb, "lua", "delete", {
    	-- 	database = "testdb",
        --     collection = "role",
        --     selector = {num=100},
        --     single = 1,
        -- })
    
        -- ret = skynet.call(mongodb, "lua", "drop", {
    	-- 	database = "testdb",
        --     collection = "role",
        -- })
        -- util.dump_table(ret)
    
    	-- ret = skynet.call(mongodb, "lua", "findOne", {
    	-- 	database = "testdb",
    	-- 	collection = "role",
    	-- 	query = {num = 100},
    	-- 	selector = {}
        -- })
        -- util.dump_table(ret)
    	
    	-- ret = skynet.call(mongodb, "lua", "findAll", {
    	-- 	database = "testdb",
    	-- 	collection = "role",
    	-- 	query = {num = 100},
    	-- 	selector = {test=1}
    	-- })
        -- util.dump_table(ret)
    
        -- local updater = {}
        -- updater["$set"] = {test = "hello world1"}			
        
        -- local allResult = skynet.call(mongodb, "lua", "update", {
        --     database = "testdb",
        --     collection = "role",
        --     selector = {num=100},
        --     update = updater,
        --     upsert = false,
        --     multi = false
        -- })
        -- util.dump_table(ret)
    
    	skynet.exit()
    end)
    

    util.lua中的全部代码:

    
    local util = {}
    
    local function get_type_first_print( t )
        local str = type(t)
        return string.upper(string.sub(str, 1, 1))..":"
    end
    
    function util.dump_table(t, prefix, indent_input, print)
        local indent = indent_input
        if indent_input == nil then
            indent = 1
        end
    
        if print == nil then
            print = _G["print"]
        end
    
        local p = nil
    
        local formatting = string.rep("    ", indent)
        if prefix ~= nil then
            formatting = prefix .. formatting
        end
    
        if t == nil then
            print(formatting.." nil")
            return
        end
    
        if type(t) ~= "table" then
            print(formatting..get_type_first_print(t)..tostring(t))
            return
        end
    
        local output_count = 0
        for k,v in pairs(t) do
            local str_k = get_type_first_print(k)
            if type(v) == "table" then
    
                print(formatting..str_k..k.." -> ")
    
                util.dump_table(v, prefix, indent + 1,print)
            else
                print(formatting..str_k..k.." -> ".. get_type_first_print(v)..tostring(v))
            end
            output_count = output_count + 1
        end
    
        if output_count == 0 then
            print(formatting.." {}")
        end
    end
    
    return util
    

    最后的最后:

    通过传入不同参数,可以通过runCommand,可以扩展我们对mongodb的操作。

    function _M.runCommand(args)
    	local db = client:getDB(args.database)
    	local result = db:runCommand(args)
    	return result
    end
    

    参考:
    lua打印table

    Mongodb 更新操作update

    展开全文
  • Skynet is a lightweight online game framework which can be used in many other fields. Build For Linux, install autoconf first for jemalloc: git clone https://github.com/cloudwu/skynet.git cd skynet ...
  • skynet框架应用 (一) skynet介绍

    万次阅读 多人点赞 2018-03-03 20:39:35
    Skynet 是一个基于C跟lua的开源服务端并发框架,这个框架是单进程多线程Actor模型。是一个轻量级的为在线游戏服务器打造的框架。 我在云风博客的基础上,把重要的知识点加上具体的example来讲解skynet如何去使用...

    1 skynet介绍

    ​ Skynet 是一个基于C跟lua的开源服务端并发框架,这个框架是单进程多线程Actor模型。是一个轻量级的为在线游戏服务器打造的框架。

    我在云风博客的基础上,把重要的知识点加上具体的example来讲解skynet如何去使用。前面的比较啰嗦一些,大家不想了解这些知识的,可以直接跳转到第二章节。

     

     

    1.1 简介

    ​ 这个系统是单进程多线程模型。

    ​ 每个服务都是严格的被动的消息驱动的,以一个统一的 callback 函数的形式交给框架。框架从消息队列里调度出接收的服务模块,找到 callback 函数入口,调用它。服务本身在没有被调度时,是不占用任何 CPU 的。

    ​ skynet虽然支持集群,但是作者云风主张能用一个节点完成尽量用一个节点,因为多节点通信方面的开销太大,如果一共有 100 个 skynet 节点,在它们启动完毕后,会建立起 9900条通讯通道。

    1.2 特点

     

    Skynet框架做两个必要的保证:

    一、一个服务的 callback 函数永远不会被并发。

    二、一个服务向另一个服务发送的消息的次序是严格保证的。

    ​ 用多线程模型来实现它。底层有一个线程消息队列,消息由三部分构成:源地址、目的地址、以及数据块。框架启动固定的多条线程,每条工作线程不断从消息队列取到消息,调用服务的 callback 函数。

    ​ 线程数应该略大于系统的 CPU 核数,以防止系统饥饿。(只要服务不直接给自己不断发新的消息,就不会有服务被饿死)

    ​ 对于目前的点对点消息,要求发送者调用 malloc 分配出消息携带数据用到的内存;由接受方处理完后调用 free 清理(由框架来做)。这样数据传递就不需要有额外的拷贝了。

    ​ 做为核心功能,Skynet 仅解决一个问题:

    ​ 把一个符合规范的 C 模块,从动态库(so 文件)中启动起来,绑定一个永不重复(即使模块退出)的数字 id 做为其 handle 。模块被称为服务(Service),服务间可以自由发送消息。每个模块可以向 Skynet 框架注册一个 callback 函数,用来接收发给它的消息。每个服务都是被一个个消息包驱动,当没有包到来的时候,它们就会处于挂起状态,对 CPU 资源零消耗。如果需要自主逻辑,则可以利用 Skynet 系统提供的 timeout 消息,定期触发。

    1.3 Actor模型

    1.3.1 Actor模型介绍

    ​ **Actor模型内部的状态由它自己维护即它内部数据只能由它自己修改(通过消息传递来进行状态修改),所以使用Actors模型进行并发编程可以很好地避免这些问题,Actor由状态(state)、行为(Behavior)和邮箱(mailBox)三部分组成**

    1. 状态(state):Actor中的状态指的是Actor对象的变量信息,状态由Actor自己管理,避免了并发环境下的锁和内存原子性等问题

    2. 行为(Behavior):行为指定的是Actor中计算逻辑,通过Actor接收到消息来改变Actor的状态

    3. 邮箱(mailBox):邮箱是Actor和Actor之间的通信桥梁,邮箱内部通过FIFO消息队列来存储发送方Actor消息,接受方Actor从邮箱队列中获取消息

    Actor的基础就是消息传递,skynet中每个服务就是一个LUA虚拟机,就是一个Actor。

    1.3.2 Actor模型好处

    1. 事件模型驱动: Actor之间的通信是异步的,即使Actor在发送消息后也无需阻塞或者等待就能够处理其他事情。

    2. 强隔离性: Actor中的方法不能由外部直接调用,所有的一切都通过消息传递进行的,从而避免了Actor之间的数据共享,想要观察到另一个Actor的状态变化只能通过消息传递进行询问。

    3. 位置透明: 无论Actor地址是在本地还是在远程机上对于代码来说都是一样的。

    4. 轻量性:Actor是非常轻量的计算单机,只需少量内存就能达到高并发。

    展开全文
  • skynet框架应用 (十二) snax框架

    千次阅读 热门讨论 2018-03-04 11:11:17
    12 snax框架​ snax 是一个方便 skynet 服务实现的简单框架。(简单是相对于 skynet 的 api 而言)​ 使用 snax 服务先要在 Config 中配置 snax 用于路径查找。每个 snax 服务都有一个用于启动服务的名字,推荐按 ...

    12 snax框架

    ​ snax 是一个方便 skynet 服务实现的简单框架。(简单是相对于 skynet 的 api 而言)

    ​ 使用 snax 服务先要在 Config 中配置 snax 用于路径查找。每个 snax 服务都有一个用于启动服务的名字,推荐按 lua 的模块命名规则,但目前不推荐在服务名中包含"点" (在路径搜索上尚未支持 . 与 / 的替换)。在启动服务时会按查找路径搜索对应的文件。

    12.1 snax服务基础API

    • 启动snax服务的API

    
    local snax = require "snax"
    snax.newservice(name, ...) --可以把一个服务启动多份。传入服务名和参数,它会返回一个对象,用于和这个启动的服务交互。如果多次调用 newservice ,即使名字相同,也会生成多份服务的实例,它们各自独立,由不同的对象区分。注意返回的不是服务地址,是一个对象。
    
    snax.uniqueservice(name, ...) --和上面 api 类似,但在一个节点上只会启动一份同名服务。如果你多次调用它,会返回相同的对象。
    
    snax.globalservice(name, ...) --和上面的 api 类似,但在整个 skynet 网络中(如果你启动了多个节点),只会有一个同名服务。
    • 查询snax服务

    
    snax.queryservice(name) --查询当前节点的具名服务,返回一个服务对象。如果服务尚未启动,那么一直阻塞等待它启动完毕。
    
    snax.queryglobal(name) --查询一个全局名字的服务,返回一个服务对象。如果服务尚未启动,那么一直阻塞等待它启动完毕。
    
    snax.self() --用来获取自己这个服务对象,与skynet.self不同,它不是地址。
    • snax服务退出

    
    snax.kill(obj, ...) --如果你想让一个 snax 服务退出,调用 
    
    snax.exit(...) --退出当前服务,它等价于 snax.kill(snax.self(), ...) 。
    • 通过snax服务地址获取snax服务对象

      对于匿名服务,你无法在别处通过名字得到和它交互的对象。如果你有这个需求,可以把对象的handle通过消息发送给别人。 handle 是一个数字,即 snax 服务的 skynet 服务地址。

    
    --把handle转换成服务对象。这里第二个参数需要传入服务的启动名,以用来了解这个服务有哪些远程方法可以供调用。当然,你也可以直接把 .type 域和 .handle 一起发送过去,而不必在源代码上约定。
    snax.bind(handle, typename) 
    • snax启动查找服务路径是config.path的snax变量来指定

    
    snax = root.."examples/?.lua;"..root.."test/?.lua;".."my_workspace/?.lua" --添加my_workspace路径

    12.2 最简单snax服务

    ​ 每个 snax 服务中都需要定义一个 init 函数,启动这个服务会调用这个函数,并把启动参数传给它。

    snax 服务还可以定义一个 exit 函数用于响应服务退出的事件,同样能接收一些参数。

    ​ 和标准的 skynet 服务不同,这些参数的类型不受限制,可以是 lua 的复杂数据类型。(而 skynet 服务受底层限制,只可以接受字符串参数)

    写一个最简单的snax服务simplesnax.lua如下:

    
    local skynet = require "skynet"
    local snax = require "skynet.snax"
    
    function init( ... )        --snax服务初始化时会调用该回调函数,可以获取到启动参数
        skynet.error ("snax server start:", ...)
    end
    
    function exit(...)  --snax服务初始化时会调用该回调函数,可以获取到退出参数
        skynet.error ("snax server exit:", ...)
    end

    ​ snax不是普通服务,需要与snax框架配合使用,必须使用snax.newservice、snax.uniqueservice、snax.globalservice这个三个函数来启动。

    ​ 在console服务中启动snax服务时,需要指定为snax:

    
    snax simplesnax nengzhong     #输入这一行
    [:0100000a] LAUNCH snlua snaxd simplesnax
    [:0100000a] snax server start: nengzhong

    12.3 snax服务请求

    ​ snax请求分为无响应请求与有响应请求。

    ​ 对snax服务发请求的方法

    
    --无响应请求,obj是snax对象,post表示无响应请求,CMD具体的请求命令,...为请求参数列表,发送完马上返回
    obj.post.CMD(...)
    --有响应请求,obj是snax对象,req表示有响应请求,CMD具体的请求命令,...为请求参数列表,发送完等待响应
    obj.req.CMD(...)

    12.3.1 snax处理无响应请求

    修改simplesnax.lua:

    
    local skynet = require "skynet"
    local snax = require "skynet.snax"
    
    function accept.hello(...) --通过obj.post.hello
        skynet.error("hello", ...)
    end
    
    function accept.quit(...)  --obj.post.quit来触发回调函数
      snax.exit(...)
      --等同snax.kill(snax.self(), ...)
    end
    
    function init( ... )        
        skynet.error("snax server start:", ...)
    end
    
    function exit(...)  
        skynet.error("snax server exit:", ...)
    end

    编写testsimplesnax.lua:

    
    local skynet = require "skynet"
    local snax = require "skynet.snax"
    
    skynet.start(function ()
        local obj = snax.newservice("simplesnax", 123, "abc", false) --启动simplessnax服务,并传递参数
        skynet.error("snax service", obj, "startup")
        
        local r = obj.post.hello(123, "abc", false) --调用simplesnax中的accept.hello方法
        skynet.error("hello return:", r)
            
        obj.post.quit("exit now") --退出服务
    end)

    运行结果:

    
    testsimplesnax
    [:0100000a] LAUNCH snlua testsimplesnax
    [:0100000b] LAUNCH snlua snaxd simplesnax
    [:0100000b] snax server start: 123 abc false
    [:0100000a] snax service [simplesnax:100000b] startup
    [:0100000a] hello return: nil #post方法没有返回值
    [:0100000b] hello 123 abc false 
    [:0100000b] snax server exit: exit now #服务退出的时候exit函数调用
    [:0100000b] KILL self

    12.3.2 处理有响应请求

    修改simplesnax.lua

    
    local skynet = require "skynet"
    local snax = require "skynet.snax"
    
    function response.echo(str) --当其他服通过obj.req.echo调用的时候,触发该回调函数,并返回应答
         skynet.error("echo", str)
        return str:upper()
    end
    
    
    function init( ... )        
        skynet.error("snax server start:", ...)
    end
    
    function exit(...)  
        skynet.error("snax server exit:", ...)
    end
    

    修改testsimplesnax.lua

    
    local skynet = require "skynet"
    local snax = require "skynet.snax"
    
    skynet.start(function ()
        local obj = snax.newservice("simplesnax", 123, "abc", false) 
        skynet.error("snax service", obj, "startup")
        
        local r = obj.req.echo("nengzhong") --调用simplesnax中的response.echo方法
        skynet.error("echo return:", r)
    
    end)

    运行结果:

    
    testsimplesnax
    [:0100000a] LAUNCH snlua testsimplesnax
    [:0100000b] LAUNCH snlua snaxd simplesnax
    [:0100000b] snax server start: 123 abc false
    [:0100000a] snax service [simplesnax:100000b] startup
    [:0100000b] echo nengzhong
    [:0100000a] echo return: NENGZHONG  #得到返回值

    12.4 snax全局唯一服

    修改testsimplesnax.lua:

    
    local skynet = require "skynet"
    local snax = require "skynet.snax"
    
    skynet.start(function ()
        local obj = snax.uniqueservice("simplesnax", 123, "abc") --启动simplessnax服务
        obj = snax.queryservice("simplesnax") --查询全局唯一服
        snax.kill(obj, 123, "abc")
            
        local gobj = snax.globalservice("simplesnax", 123, "abc") --启动simplessnax服务
        gobj = snax.queryglobal("simplesnax") --查询全节点全局唯一服
        snax.kill(gobj, 123, "abc")
            
        skynet.exit()
    end)

    运行结果:

    
    testsimplesnax
    [:0100000a] LAUNCH snlua testsimplesnax
    [:0100000b] LAUNCH snlua snaxd simplesnax
    [:0100000b] snax server start: 123 abc
    [:0100000b] snax server exit: 123 abc
    [:0100000b] KILL self
    [:0100000c] LAUNCH snlua snaxd simplesnax
    [:0100000c] snax server start: 123 abc
    [:0100000c] snax server exit: 123 abc
    [:0100000c] KILL self
    [:0100000a] KILL self

    12.5 snax服务热更

    ​ snax 是支持热更新的(只能热更新 snax 框架编写的 lua 服务)。但热更新更多的用途是做不停机的 bug 修复,不应用于常规的版本更新。所以,热更新的 api 被设计成下面这个样子。更适合打补丁。

    你可以通过 snax.hotfix(obj, patchcode) 来向 obj 提交一个 patch 。

    12.5.1 函数patch

    simplesnax.lua

    
    local skynet = require "skynet"
    local snax = require "skynet.snax"
    local i = 10
    gname = "nengzhong"
    function accept.hello(...) --通过obj.post.hello
        skynet.error("hello", i,  gname, ...)
    end
    
    function accept.quit(...)  --obj.post.quit来触发回调函数
      snax.exit(...)
      --等同snax.kill(snax.self(), ...)
    end
    
    function init( ... )        
        skynet.error("snax server start:", ...)
    end
    
    function exit(...)  
        skynet.error("snax server exit:", ...)
    end

    testhotfix.lua

    
    local skynet = require "skynet"
    local snax = require "skynet.snax"
    
    skynet.start(function ()
        local obj = snax.newservice("simplesnax") --启动simplessnax服务
    
        obj.post.hello()  --未更新之前调用一次
    
        local r = snax.hotfix(obj, [[
            
            function accept.hello(...)
                print("fix hello", i, gname, ...) --skynet.error不能用了
            end
           
            ]])
        skynet.error("hotfix return:", r)
    
        obj.post.hello() --更新之后再调用一次
    
        obj.post.quit() --没更新quit函数,还是能调用
    
        skynet.exit()
    end)

    运行结果:

    
    testhotfix
    [:0100000a] LAUNCH snlua testhotfix
    [:0100000b] LAUNCH snlua snaxd simplesnax
    [:0100000b] snax server start:
    [:0100000b] hello 10 nengzhong
    fix hello   nil nengzhong   #热更成功,但是局部变量成了nil,全局变量gname还存在
    [:0100000a] hotfix return: nil
    [:0100000a] KILL self
    [:0100000b] snax server exit:   #热更完quit函数还存在
    [:0100000b] KILL self

    12.5.2 local变量patch

    ​ 上面的结果,我们发现local i不能用了,skynet也不能用了,这是因为local变量没有映射,需要我们自己来映射一下。

    修改testhotfix.lua

    
    
    local skynet = require "skynet"
    local snax = require "skynet.snax"
    
    skynet.start(function ()
        local obj = snax.newservice("simplesnax") --启动simplessnax服务
    
        obj.post.hello()  --未更新之前调用一次
    
        local r = snax.hotfix(obj, [[
            local skynet
            local i
            function accept.hello(...)
                skynet.error("fix hello", i, gname, ...)
            end
            ]])
        skynet.error("hotfix return:", r)
    
        obj.post.hello() --更新之后再调用一次
        obj.post.quit() --没更新quit函数,还是能调用
    
        skynet.exit()
    end)

    运行结果:

    
    testhotfix
    [:0100000a] LAUNCH snlua testhotfix
    [:0100000b] LAUNCH snlua snaxd simplesnax
    [:0100000b] snax server start:
    [:0100000b] hello 10 nengzhong
    [:0100000a] hotfix return: nil
    [:0100000a] KILL self
    [:0100000b] fix hello 10  nengzhong #变量skynet,i映射成功
    [:0100000b] snax server exit:
    [:0100000b] KILL self

    ​ 在 patch 中声明的 local skynet 和 local i在之前的版本中也有同名的 local 变量。 snax 的热更新机制会重新映射这些 local 变量。让 patch 中的新函数对应已有的 local 变量,所以你可以安全的继承服务的内部状态。

    12.5.3 修改snax服务线上状态

    ​ patch 中可以包含一个 function hotfix(...) 函数,在 patch 提交后立刻执行。这个函数可以用来查看或修改 snax 服务的线上状态(因为 local 变量会被关联)。hotfix 的函数返回值会传递给 snax.hotfix 的调用者。

    修改testhotfix.lua

    
    local skynet = require "skynet"
    local snax = require "skynet.snax"
    
    skynet.start(function ()
        local obj = snax.newservice("simplesnax") --启动simplessnax服务
    
        obj.post.hello()  --未更新之前调用一次
    
        local r = snax.hotfix(obj, [[
            local skynet
            local i
            function accept.hello(...)
                skynet.error("fix hello", i, gname, ...)
            end
           
            function hotfix(...) 
                local temp = i
                i = 100
                return temp
            end
            ]])
        skynet.error("hotfix return:", r)
    
        obj.post.hello() --更新之后再调用一次
        obj.post.quit() --没更新quit函数,还是能调用
        skynet.exit()
    end)

    运行结果:

    
    testhotfix
    [:0100000a] LAUNCH snlua testhotfix
    [:0100000b] LAUNCH snlua snaxd simplesnax
    [:0100000b] snax server start:
    [:0100000b] hello 10 nengzhong
    [:0100000a] hotfix return: 10 #hotfix有返回值
    [:0100000a] KILL self
    [:0100000b] fix hello 100 nengzhong #修改了local i的值
    [:0100000b] snax server exit:
    [:0100000b] KILL self

    ​ 所以,你也可以提交一个仅包含 hotfix 函数的 patch ,而不修改任何代码。这样的 patch 通常用于查看 snax 服务的内部状态(内部 local 变量的值),或用于修改它们。

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,354
精华内容 541
关键字:

skynet框架应用