服务端Skynet(五)——如何搭建一个实例
文章目录
- 服务端Skynet(五)——如何搭建一个实例
- 1、配置文件
- 2、服务消息分发与回应(call/send)
- 3、通信(server/client)
- 4、Mysql连接
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 service2 = 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)
启动前数据库数据:
发信息后
数据库截图