服务端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"

--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"

我们可以把两个文件合并为一个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"


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)
--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)

案例测试:

在这里插入图片描述

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)
--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")
--main.lua
local skynet = require "skynet"

-- 启动服务(启动函数)
skynet.start(function()
	-- 启动函数里调用Skynet API开发各种服务
	print("======Server start=======")

	skynet.newservice("socket")
	skynet.exit()
end)
运行客户端:./skynet/3rd/lua/lua ./test/service/client.lua 

运行服务端:./skynet/skynet ./test/config

实例展示:

在这里插入图片描述

或者客户端:telnet 127.0.0.1 7777

在这里插入图片描述

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));

修改一下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)

启动前数据库数据:

在这里插入图片描述

发信息后

在这里插入图片描述

数据库截图

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值