• 服务端Skynet(五)——如何搭建一个实例


    服务端Skynet(五)——如何搭建一个实例

    1、配置文件

    ​ 搭建一个实例 主要看 config 文件的设置,如下:

    --config  
    
    include "config.path"			-- 库文件、服务文件位置
    
    -- preload = "./examples/preload.lua"	-- run preload.lua before every lua service run
    thread = 8						-- 启动多少个线程
    logger = nil					-- 输出日志保存到logger项指定的文件中
    logpath = "."
    harbor = 1						-- skynet初期版本提供了“master/slave”集群模式,后来又提供了更适用的“cluster”集群模式。
    address = "127.0.0.1:2526"
    master = "127.0.0.1:2013"
    start = "main"	-- main script	  -- 主服务入口,路径为config文件同目录下
    bootstrap = "snlua bootstrap"	-- The service for bootstrap
    standalone = "0.0.0.0:2013"
    -- snax_interface_g = "snax_g"
    cpath = root.."cservice/?.so"	   -- -- 用c编写的服务模块的位置
    -- daemon = "./skynet.pid"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    --config.path
    root = "./"
    luaservice = root.."service/?.lua;"..root.."test/?.lua;"..root.."examples/?.lua;"..root.."test/?/init.lua"
    lualoader = root .. "lualib/loader.lua"
    lua_path = root.."lualib/?.lua;"..root.."lualib/?/init.lua"
    lua_cpath = root .. "luaclib/?.so"
    snax = root.."examples/?.lua;"..root.."test/?.lua"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    我们可以把两个文件合并为一个config,再根据实例项目的目录设置相应的路径,例如我的实例路径:

    在这里插入图片描述

    -- 我的实例  config    其中skynet为同层所以相关路径要加一层  代码主要放在test 和 test/service 所以luaservice要加一下
    root = "/home/XXX/skynet_test/"		--测试目录
    thread = 8
    logger = nil
    logpath = "."
    harbor = 1
    address = "127.0.0.1:2526"
    master = "127.0.0.1:2013"
    start = "main"	-- main script
    bootstrap = "snlua bootstrap"	-- 启动的第一个服务以及其启动参数 service/bootstrap.lua
    standalone = "0.0.0.0:2013"
    luaservice = root.."test/?.lua;"..root.."test/service/?.lua;"..root.."skynet/service/?.lua;"
    lualoader = root .. "skynet/lualib/loader.lua"
    lua_path = root.."skynet/lualib/?.lua;"..root.."skynet/lualib/?/init.lua"
    lua_cpath = root .. "skynet/luaclib/?.so"
    -- preload = "./example1/preload.lua"	-- run preload.lua before every lua service run
    -- snax = root.."example1/?.lua;"..root.."test/?.lua"
    -- snax_interface_g = "snax_g"
    cpath = root.."skynet/cservice/?.so"
    -- daemon = "./skynet.pid"
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2、服务消息分发与回应(call/send)

    skynet.send(address, typename, …) 把一条类别为 typename 的消息发送给 address 。它会先经过事先注册的 pack 函数打包 … 的内容。skynet.send 是一条非阻塞 API ,发送完消息后,coroutine 会继续向下运行,这期间服务不会重入。

    skynet.call(address, typename, …) 会在内部生成一个唯一 session ,并向 address 提起请求,并阻塞等待对 session 的回应(可以不由 address 回应)。当消息回应后,还会通过之前注册的 unpack 函数解包。尤其需要留意的是,skynet.call 仅仅阻塞住当前的 coroutine ,而没有阻塞整个服务。在等待回应期间,服务照样可以响应其他请求。所以,尤其要注意,在 skynet.call 之前获得的服务内的状态,到返回后,很有可能改变

    skynet.ret(message, size) 回应一个消息,它会将 message size 对应的消息附上当前消息的 session ,以及 skynet.PTYPE_RESPONSE 这个类别,发送给当前消息的来源 source

    ​ 由于 skynet 中最常用的消息类别是 lua ,这种消息是经过 skynet.pack 打包的,所以惯用法是 skynet.ret(skynet.pack(…)) 。skynet.pack(…) 返回一个 lightuserdata 和一个长度,符合 skynet.ret 的参数需求;与之对应的是 skynet.unpack(message, size) 它可以把一个 C 指针加长度的消息解码成一组 Lua 对象。

    -- service.lua
    local skynet = require "skynet"
    
    require"skynet.manager" -- 引入 skynet.register
    
    local db = {}
    
    local command = {}
    
    
    function command.get(key)
        print("command get :", key)
        return db[key]
    end
    
    function command.set(key,value)
        print("command set:".. key .. "  status:".. value)
        db[key] = value
        local last = db[key]
        return last
    end
    
    skynet.start(function()
        print("==================service start====================")
        skynet.dispatch("lua", function(session, address, cmd,...)
            local f = command[cmd]
            if f then
                -- 回应一个消息可以使用 skynet.ret(message, size) 。
                -- 它会将 message size 对应的消息附上当前消息的 session ,以及 skynet.PTYPE_RESPONSE 这个类别,发送给当前消息的来源 source 
                skynet.ret(skynet.pack(f(...))) --回应消息
            else
                error(string.format("Unknown command %s", tostring(cmd)))
            end
        end)
    
        --可以为自己注册一个别名。(别名必须在 32 个字符以内)
    	skynet.register "SERVICE2"
    end)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    --main.lua
    local skynet = require "skynet"
    
    -- 启动服务(启动函数)
    skynet.start(function()
    	-- 启动函数里调用Skynet API开发各种服务
    	print("======Server main start=======")
    	-- skynet.newservice(name, ...)启动一个新的 Lua 服务(服务脚本文件名) 沙盒
    	local service = skynet.newservice("service")
    
    	-- 向service服务发出请求,设置game_0 = runing
    	skynet.call(service,"lua","set","game_0","runing")	
    
    	-- 向service服务发出请求,获取key1的值
    	local game_status = skynet.call(service,"lua","get","game_0")
    
    	print("service game_status : ",game_status)
    	
    	-- 退出当前的服务
    	-- skynet.exit 之后的代码都不会被运行。而且,当前服务被阻塞住的 coroutine 也会立刻中断退出。
    	skynet.exit()
    end)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    案例测试:

    在这里插入图片描述

    3、通信(server/client)

    参考案例:官方的例子(test/testsocket.lua)

    local skynet = require "skynet"
    local socket = require "skynet.socket"
    
    local clients = {}  --记录连接的客户端
    
    -- 读取客户端消息 并输出
    local function echo(id)
        -- 每当 accept 函数获得一个新的 socket id 后,并不会立即收到这个 socket 上的数据。这是因为,我们有时会希望把这个 socket 的操作权转让给别的服务去处理。
        -- 任何一个服务只有在调用 socket.start(id) 之后,才可以收到这个 socket 上的数据。
        socket.start(id)
        clients[id] = true
        while true do
            -- 读取客户端发过来的数据
            local str = socket.read(id)
            if str then
                -- 直接打印接收到的数据
                local _str = "[client".. id .. "] Send ".. str
                print(_str)
    
                for k,v in pairs(clients) do --广播
    				socket.write(k, _str)
                end
                -- socket.write(id, _str)  --发给 客户端_id 接收的消息
            else
                socket.close(id)
                if clients[fd] then
                    clients[fd] = nil
                end
                return 
            end
        end
    end
    
    skynet.start(function()
        print("==========Socket Start=========")
        -- 监听一个端口,返回一个 id ,供 start 使用
        local socket_id = socket.listen("127.0.0.1", 7777)
        print("Listen socket :", "127.0.0.1", 7777)
    
        socket.start(socket_id, function(id,addr)
            -- 接收到客户端连接或发送消息
            print("connect from" .. addr .. " [client ".. id .. " ]")
    
            -- 处理消息
            echo(id)
        end)
    end)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    --client.lua
    package.cpath = "./skynet/luaclib/?.so"
    package.path = "./skynet/lualib/?.lua;./test/service/?.lua"
    
    local socket = require "client.socket"
    
    local fd = assert(socket.connect("127.0.0.1", 7777))
    
    socket.send(fd, "77777")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    --main.lua
    local skynet = require "skynet"
    
    -- 启动服务(启动函数)
    skynet.start(function()
    	-- 启动函数里调用Skynet API开发各种服务
    	print("======Server start=======")
    
    	skynet.newservice("socket")
    	skynet.exit()
    end)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    运行客户端:./skynet/3rd/lua/lua ./test/service/client.lua 
    
    运行服务端:./skynet/skynet ./test/config
    
    • 1
    • 2
    • 3

    实例展示:

    在这里插入图片描述

    或者客户端:telnet 127.0.0.1 7777
    
    • 1

    在这里插入图片描述

    4、Mysql连接

    参考案例:官方的例子(test/testmysql.lua)

    -- db test_db   创建测试数据库
    create table msgs (
    	id int not null auto_increment,
    	client_id varchar(30) not null,
    	content varchar(255) not null,
    	primary key (id));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    修改一下socket.lua 文件 将发言数据存到数据库

    local skynet = require "skynet"
    local socket = require "skynet.socket"
    local mysql = require "skynet.db.mysql"
    
    local clients = {}  --记录连接的客户端
    local db = {}       --数据库连接
    
    -- 读取客户端消息 并输出
    local function echo(client_id)
        -- 每当 accept 函数获得一个新的 socket client_id 后,并不会立即收到这个 socket 上的数据。这是因为,我们有时会希望把这个 socket 的操作权转让给别的服务去处理。
        -- 任何一个服务只有在调用 socket.start(client_id) 之后,才可以收到这个 socket 上的数据。
        socket.start(client_id)
        clients[client_id] = true
        while true do
            -- 读取客户端发过来的数据
            local str = socket.read(client_id)
            if str then
                if str ==  "get\r\n" then
                    --获取留言信息
                    -- local _str = db:query("select * from msgs")
                    -- for i,v in pairs(_str) do
                    --     local content = "[client".. v.client_id .. "] : "..v.content.."\r\n"
    				-- 	socket.write (client_id, content)
                    -- end
    
                    --获取留言信息
                    local _str = db:query("select * from msgs")
                    local content = "==========================留言版=========================\r\n"
                    for i,v in pairs(_str) do
                        content = content .. "[client".. v.client_id .. "] : "..v.content.."\r\n"
                    end
                    content = content .. "==========================留言版=========================\r\n"
                    socket.write (client_id, content)
                else
    
                    -- 直接打印接收到的数据
                    local _str = "[client".. client_id .. "] : ".. str
                    print(_str)
                    for k,v in pairs(clients) do --广播
                    	socket.write(k, _str)
                    end
                    -- socket.write(client_id, _str)  --发给 客户端_client_id 接收的消息
                    --留言存入数据库
                    db:query("insert into msgs (client_id, content) values (\'"..client_id.."\',\'"..str.."\')")
                end
            else
                socket.close(client_id)
                if clients[fd] then
                    clients[fd] = nil
                end
                return 
            end
        end
    end
    
    skynet.start(function()
        print("==========Socket Start=========")
        -- 监听一个端口,返回一个 client_id ,供 start 使用
        local socket_client_id = socket.listen("127.0.0.1", 7777)
        print("Listen socket :", "127.0.0.1", 7777)
    
        socket.start(socket_client_id, function(client_id,addr)
            -- 接收到客户端连接或发送消息
            print("connect from" .. addr .. " [client ".. client_id .. " ]")
    
            -- 处理消息
            echo(client_id)
        end)
    
    
        --连接数据库
        db = mysql.connect(
            {
                host = "127.0.0.1",
                port = 3306,
                database = "test_db",
                user = "root",
                password = "121212",
                max_packet_size = 1024 * 1024,
                on_connect = nil
            }
        )
    end)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    启动前数据库数据:

    在这里插入图片描述

    发信息后

    在这里插入图片描述

    数据库截图

    在这里插入图片描述

  • 相关阅读:
    “一个有趣的C语言代码”分析
    PyTorch安装步骤
    【前端】常用属性及实例
    众和策略:小盘和大盘的关系?
    发过的朋友圈怎么再快速发一次?
    物联网开发笔记(41)- 使用Micropython开发ESP32开发板之控制4*4矩阵键盘
    mysql—面试50题—1
    线上教育服务的最优选是小程序化?
    第15届蓝桥STEMA测评真题剖析-2023年10月29日Scratch编程初中级组
    不同版本vue安装vue-router
  • 原文地址:https://blog.csdn.net/weixin_43730892/article/details/127906380