Unity3D|【游戏开发实战】手把手教你从零跑一个Skynet,详细教程,含案例讲解(服务端 | Skynet | Ubuntu)
文章目录
-
-
- 一、前言
- 二、关于Skynet
- 三、Ubuntu虚拟机
-
- 1、Ubuntu系统镜像下载
- 2、VirtualBox虚拟机软件
-
- 2.1、VirtualBox下载
- 2.2、VirtualBox安装
- 2.3、创建虚拟机
- 3、载入Ubuntu iso镜像
- 4、Ubuntu系统安装过程
- 四、安装必要的工具
-
- 1、安装git
- 2、安装autoconf
- 3、安装gcc
- 五、下载Skynet源码
- 六、编译Skynet源码
- 七、运行Skynet案例
- 八、写个Demo
-
- 1、配置文件
- 2、规范目录结构
- 3、自己写个配置文件
- 4、主服务
- 5、写个打工服务
- 6、在主服务中启动打工服务
- 7、在主服务中给打工服务发消息
- 8、封装服务类
- 9、重写打工服务
- 10、买猫粮服务
- 九、补充
-
- 1、网络模块
- 2、节点集群
- 3、数据库模块
-
- 3.1、安装MySQL
- 3.2、启动MySQL
- 3.3、关闭MySQL
- 3.4、登录MySQL
- 3.5、在skynet中操作数据库
- 十、完毕
-
一、前言
嗨,大家好,我是新发。
认识我的朋友都知道我是一名
Unity
游戏开发工程师,也就是我平时做的是客户端部分的开发,其实以前我是一名服务端开发工程师,后来因为工作原因,转岗做了Unity
客户端开发,然后就一直干到现在。最近,我在搞服务端的
skynet
框架,看看以后自己做些作品(skynet
框架服务端+Unity
客户端)。今天呢,我就先把skynet
环境搞一下,讲讲流程,也方便想学习的同学,话不多说,我们开始吧~二、关于Skynet
skynet
是一个轻量级的网络游戏框架,也可用于许多其他领域。文章图片
建议大家看下云风的《Skynet设计综述》,这里我不过多赘述,主要讲讲操作流程~
三、Ubuntu虚拟机
skynet
需要运行在linux
或macos
系统中,这里作为演示,我使用Ubuntu
虚拟机。下面我讲下Ubuntu
虚拟机的安装过程。1、Ubuntu系统镜像下载 首先我们需要先下载
Ubuntu
系统的iso
文件,下面这些地址都可以下载,大家选择一个即可:网易开源镜像:http://mirrors.163.com/ubuntu-releases/我以
Ubuntu
官方:http://releases.ubuntu.com/
Ubuntu
中国官网:https://ubuntu.com/download/alternative-downloads
中科开源镜像:http://mirrors.ustc.edu.cn/ubuntu-releases/
阿里开源镜像:http://mirrors.aliyun.com/ubuntu-releases/
浙江大学开源镜像:http://mirrors.zju.edu.cn/ubuntu-releases/
Ubuntu 16.04.7
版本为例,地址:http://mirrors.163.com/ubuntu-releases/16.04.7/文章图片
把
iso
文件下载到本地,文章图片
2、VirtualBox虚拟机软件 有了
iso
文件,需要将其安装到虚拟机中,而虚拟机需要运行在虚拟机软件上,所以,我们还需要先安装一个虚拟机软件。虚拟机软件大家常用的是
VMWare
,这里我强烈推荐另一款虚拟机软件:VirtualBox
,它轻量、开源免费,对于个人学习使用完全足够,五星推荐~【Unity3D|【游戏开发实战】手把手教你从零跑一个Skynet,详细教程,含案例讲解(服务端 | Skynet | Ubuntu)】关于VirtualBox:2.1、VirtualBox下载
VirtualBox
是一款开源虚拟机软件。VirtualBox
是由德国Innotek
公司开发,由Sun Microsystems
公司出品的软件,使用Qt
编写,在Sun
被Oracle
收购后正式更名成Oracle VM VirtualBox
。
VirtualBox
号称是最强的免费虚拟机软件,它不仅具有丰富的特色,而且性能也很优异!它简单易用,可虚拟的系统包括Windows
(从Windows 3.1
到Windows 10
、Windows Server 2012
,所有的Windows
系统都支持)、Mac OS X
、Linux
、OpenBSD
、Solaris
、IBM OS2
甚至Android
等操作系统!使用者可以在VirtualBox
上安装并且运行上述的这些操作系统!
VirtualBox
我们可以从官网下载到,地址:https://www.virtualbox.org/文章图片
选择
windows
版本,点击下载,文章图片
下载完毕,
文章图片
2.2、VirtualBox安装 双击安装包运行安装,过程没有什么特别的,这里不赘述~
安装成功后打开
VirtualBox
,界面如下:文章图片
2.3、创建虚拟机 点击菜单
控制/新建
,文章图片
填写虚拟机名称,设置虚拟机保存路径,如下,我设置为
E:\ubuntu16
,文章图片
设置内存大小,建议分配
2G
内存,文章图片
创建虚拟硬盘,
文章图片
文章图片
文章图片
建议分配
10G
的虚拟硬盘空间,文章图片
虚拟机创建完成,如下
文章图片
3、载入Ubuntu iso镜像 点击启动虚拟机,会提示选择启动盘,点击下面的小按钮,
文章图片
点击注册,
文章图片
选择我们刚刚下载的
iso
系统镜像文件,打开,可以看到列表中出现了我们的镜像,选中它,文章图片
点击启动,即可进入系统安装。
4、Ubuntu系统安装过程 点击
Install Ubuntu
,文章图片
点击
Continue
,文章图片
点击
Install Now
,文章图片
此时会弹个提示框,点击
Continue
,文章图片
时区填写
China Time
,然后点击Continue
,文章图片
语言默认
English
,点击Continue
,文章图片
接着输入账号密码,后面进入系统的时候要用到,这里提示我的密码弱(
Weak password
),由于只是自己学习使用,密码弱也没什么关系,点击Continue
,文章图片
接着就是耐心等待它安装,
文章图片
完成后,会提示需要重启,点击
Restart Now
,文章图片
进入下面这个界面时,按一下回车键,
文章图片
输入刚刚设置的密码,
文章图片
顺利进入系统,
文章图片
四、安装必要的工具
1、安装git 我们需要通过
git
来下载skynet
,所以必须先安装git
,我们先打开终端,如下:文章图片
在终端输入下面的命令:(注意按回车后需要输入一次密码,并且密码不会显示出来,不要怀疑你的键盘)
sudo apt-get install git
如下:
文章图片
安装完毕后,输入下面的命令检查下
git
是否安装成功了,git --version
如果输出了版本号,则说明
git
已经安装成功了,文章图片
2、安装autoconf 编译
skynet
需要用到autoconf
,在终端输入下面的命令来安装autoconf
,sudo apt-get install autoconf
如下:
文章图片
安装完毕后,输入
autoconf --version
按回车,如果能输出版本号,则说明安装成功了,文章图片
3、安装gcc 编译
skynet
需要用到gcc
,因为Ubuntu
默认安装了gcc
,所以我们这里就不用重复安装了,可以在终端中输入gcc --version
,如果能输出版本号,则说明已经安装了gcc
,文章图片
如果提示没有
gcc
,则执行sudo apt-get install gcc
进行安装即可。五、下载Skynet源码
在终端中执行下面的命令,
git clone https://gitee.com/mirrors/skynet.git
如下:
文章图片
下载完毕后,我们打开文件夹浏览器,可以在
Home
目录中看到多了一个skynet
文件夹,文章图片
进入
skynet
文件夹,就可以看到框架源码啦,文章图片
六、编译Skynet源码
在终端中进入
skynet
目录,cd skynet
如下:
文章图片
然后执行下面的命令:
make linux
此处你可能会报错,如下:
文章图片
这是因为过程中去
gitub
下载jemalloc
失败了,可以多试几次,编译成功后显示的输出内容如下:文章图片
我们可以在
skynet
目录中看到生成了一个可执行文件:skynet
,如下:文章图片
七、运行Skynet案例
在终端中进入
skynet
目录后,执行下面的命令,./skynet example/config
如下,可以看到服务启动成功了,
文章图片
接下来,我们开启一个新的终端,
文章图片
运行一个客户端来测试一下,先
cd skynet
进入目录,然后执行如下命令:
./3rd/lua/lua example/client.lua
如下,每隔5秒就会给服务端发送一个
heartbeat
心跳包,文章图片
我们可以输入
hello
,服务器会回应一个world
,如下:文章图片
八、写个Demo
想要自己写个
Demo
,得先知道skynet
是如何工作的。1、配置文件 运行
skynet
时,需要制定一个配置文件,例:./skynet example/config
我们先看看这个
config
文件里面是啥,进入examples
目录,打开config
文件,文章图片
config
文件内容如下:include "config.path"-- preload = "./examples/preload.lua" -- run preload.lua before every lua service run
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" -- The service for bootstrap
standalone = "0.0.0.0:2013"
-- snax_interface_g = "snax_g"
cpath = root.."cservice/?.so"
-- daemon = "./skynet.pid"
第一行引用了
config.path
文件,我们打开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"
现在,我们把两个文件合在一起看,如下:
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"-- preload = "./examples/preload.lua" -- run preload.lua before every lua service run
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" -- The service for bootstrap
standalone = "0.0.0.0:2013"
-- snax_interface_g = "snax_g"
cpath = root.."cservice/?.so"
-- daemon = "./skynet.pid"
我这里对几个重要的参数做一下说明:
参数 | 描述 |
---|---|
luaservice |
服务脚本路径,包括skynet 框架自带的一些服务和自己写的服务 |
lualoader |
lua 脚本加载器,指定skynet 的loader.lua |
lua_path |
程序加载lua 脚本时,会搜索这个lua_path 配置的路径 |
lua_cpath |
用C 语言编写的程序库(.so 文件)的路径 |
thread |
启用的工作线程数量,一般配置为CPU 核心数 |
harbor |
主从节点模式。skynet 初期提供了master/slave 集群模式,后来提供了更适用的cluster 集群模式,建议使用cluster 模式,配0 |
start |
主服务入口 |
cpath |
用C 语言编写的服务模块的路径 |
文章图片
2、规范目录结构 上面介绍了配置文件,现在我们可以自己写一个配置文件啦,不过,实际项目中,一般会先规范一下目录结构,我们把根目录重命名为
game
,新建一些子文件夹:文件夹 | 说明 |
---|---|
etc |
存放配置文件 |
luaclib |
存放一些C 模块(.so 文件) |
lualib |
存放lua 模块 |
service |
存放各服务的lua 代码 |
skynet |
存放skynet 框架(存放我们刚刚下载的skynet 框架源码) |
文章图片
3、自己写个配置文件 我们在
etc
目录中新建一个config.node1
配置,如下:文章图片
因为我们把
skynet
框架源码丢在了skynet
子目录中,所以我们配置路径的时候需要加多一层skynet
,最终config.node1
配置如下:thread = 8
cpath = "./skynet/cservice/?.so"
bootstrap = "snlua bootstrap"start = "main"
harbor = 0lualoader = "./skynet/lualib/loader.lua"luaservice = "./service/?.lua;
" .. "./service/?/init.lua;
" .. "./skynet/service/?.lua;
"lua_path = "./etc/?.lua;
" .. "./lualib/?.lua;
" .. "./skynet/lualib/?.lua;
" .. "./skynet/lualib/?/init.lua;
"lua_cpath = "./luaclib/?.so;
" .. "./skynet/luaclib/?.so"
4、主服务 上面我们配置的主服务是
main
,start = "main"
它会去配置的
luaservice
路径中查找一个main.lua
脚本,luaservice = "./service/?.lua;
" .. "./service/?/init.lua;
" .. "./skynet/service/?.lua;
"
框架会去启动这个
main
服务,我们现在还没有这个main.lua
脚本,现在我们就来写这个main.lua
脚本吧~进入
service
目录,创建main.lua
脚本,代码如下:local skynet = require "skynet"
skynet.start(function()
skynet.error("[start main] hello world")
-- TODO 启动其他服务
skynet.exit()
end)
上面我们用到了
skynet
的三个API
,如下:文章图片
有同学会问了,明明说三个
API
,怎么列了四个,那个newservice(name, ...)
没看到呀!因为
main
是主服务,它是由框架来启动的,所以是框架帮我们调用了newservice
,如果我们想在主服务中启动其他服务,就要自己调用newservice
了。现在我们测试一下,打开终端,进入
game
目录,然后执行命令:./skynet/skynet etc/config.node1
运行效果如下,可以看到
main.lua
脚本被执行了,输出了[start main] hello world
,文章图片
5、写个打工服务 服务脚本统一放在
service
目录中,以服务名为文件夹名字创建子目录,打工服务我们取名为worker
,所以,我们在service
文件夹中新建一个worker
目录,文章图片
进入
worker
目录,新建一个init.lua
脚本,文章图片
init.lua
脚本需要实现服务的逻辑,文章图片
init.lua
代码如下,-- service/worker/init.lua脚本local skynet = require "skynet"-- 消息响应函数表
local CMD = {
}
-- 服务名
local worker_name = ""
-- 服务id
local worker_id = ""
-- 工钱
local money = 0
-- 是否在工作
local isworking = false-- 每帧调用,一帧的时间是0.2秒
local function update(frame)
if isworking then
money = money + 1
skynet.error(worker_name .. tostring(worker_id) .. ", money: " .. tostring(money))
end
end-- 定时器,每隔0.2秒调用一次update函数
local function timer()
local stime = skynet.now()
local frame = 0
while true do
frame = frame + 1
local isok, err = pcall(update, frame)
if not isok then
skynet.error(err)
end
local etime = skynet.now()
-- 保证0.2秒
local waittime = frame * 20 - (etime - stime)
if waittime <= 0 then
waittime = 2
end
skynet.sleep(waittime)
end
end-- 初始化
local function init(name, id)
worker_name = name
worker_id = id
end-- 开始工作
function CMD.start_work(source)
isworking = true
end-- 停止工作
function CMD.stop_work(source)
isworking = false
end-- 调用初始化函数,...是不定参数,会从skynet.newservice的第二个参数开始透传过来
init(...)skynet.start(function ()
-- 消息分发
skynet.dispatch("lua", function (session, source, cmd, ...)
-- 从CMD这个表中查找是否有定义响应函数,如果有,则触发响应函数
local func = CMD[cmd]
if func then
func(source, ...)
end
end) -- 启动定时器
skynet.fork(timer)
end)
注:这里对代码说明一下,6、在主服务中启动打工服务 我们回到主服务timer
定时器函数中,waittime
代表每次循环等待的时间,由于程序有可能会卡住,我们很难保证 “每隔0.2秒调用一次update
” 是精确的,update
函数本身执行也需要时间,所以等待时间是0.2
减去执行时间,执行时间就是etime - stime
。
main.lua
脚本中,添加一句skynet.newservice
调用,如下:-- main.lua脚本local skynet = require "skynet"skynet.start(function ()
skynet.error("[start main] hello world") -- 启动打工服务,其中第二个参数和第三个参数会透传给service/worker/init.lua脚本
local worker1 = skynet.newservice("worker", "worker", 1)skynet.exit()
end)
现在我们测试一下,在
game
目录中执行命令./skynet/skynet etc/config.node1
运行效果如下,可以看到启动了一个
worker
服务,文章图片
有同学可能会问了,我们调用
skynet.newservice
时第一个参数是worker
,框架怎么知道会去执行service/worker/init.lua
脚本呢?还记得我们的
config.node1
配置吗,里面的luaservice
我们配置了"./service/?/init.lua;
"
,如下:-- config.node1配置luaservice = "./service/?.lua;
" .. "./service/?/init.lua;
" .. "./skynet/service/?.lua;
"
其中,
?
符号会匹配服务名,也就是说,当我们调用skynet.newservice("worker")
时,框架先去检查./service/worker.lua
脚本是否存在,发现不存在,于是接着检查./service/worker/init.lua
脚本,发现存在,于是执行./service/worker/init.lua
脚本作为worker
服务,当然,如果找不到,它就会去检查./skynet/service/worker.lua
是否存在了。另外,
newservice
的函数原型是newservice(name, ...)
,我们调用skynet.newservice
时可以透传一些参数给服务,比如我们上面的-- main.lua脚本local worker1 = skynet.newservice("worker", "worker", 1)
第二个参数和第三个参数就会透传给
init.lua
脚本,我们在init.lua
脚本中可以取出来缓存起来,如下:-- service/worker/init.lua脚本-- 服务名
local worker_name = ""
-- 服务id
local worker_id = ""local function init(name, id)
worker_name = name
worker_id = id
endinit(...)
7、在主服务中给打工服务发消息 打工服务中我们定义了两个消息:
start_work
和stop_work
,现在我们在主服务中给打工服务发送消息,添加skynet.send
调用,如下:local skynet = require "skynet"skynet.start(function ()
skynet.error("[start main] hello world")
-- 启动打工服务,其中第二个参数和第三个参数会透传给service/worker/init.lua脚本
local worker1 = skynet.newservice("worker", "worker", 1)
-- 开始工作
skynet.send(worker1, "lua", "start_work")
-- 主服务休息2秒,注意,这里是主服务休息2秒,并不会卡住worker服务
skynet.sleep(200)
-- 停止工作
skynet.send(worker1, "lua", "stop_work")skynet.exit()
end)
我们再次执行命令
./skynet/skynet etc/config.node1
运行效果如下,可以看到打工服务开始工作了,
2秒
赚了10
块钱~文章图片
8、封装服务类 假设我们现在要再写一个买猫粮的服务,这个时候,可以按照上面的打工服务写一个服务。事实上,每个服务都有一些通用的变量和方法,我们可以封装一个
service
类,方便复用减少代码量。我们在
lualib
目录中新建一个service.lua
脚本,文章图片
service.lua
代码如下,local skynet = require "skynet"
local cluster = require "skynet.cluster"-- 封装服务类
local M = { -- 服务名
name = "",
-- 服务id
id = 0,
-- 退出
exit = nil,
-- 初始化
init = nil,
-- 消息响应函数表
resp = {
},
}-- 输出堆栈
local function tracback(err)
skynet.error(tostring(err))
skynet.error(debug.traceback())
end-- 消息分发
local dispatch = function (session, address, cmd, ...)
-- 从resp表中查找是否存在消息的响应函数
local func = M.resp[cmd]
if not func then
skynet.ret()
return
end
-- 调用响应函数
local ret = table.pack(xpcall(func, tracback, address, ...))
local isok = ret[1]if not isok then
skynet.ret()
return
endskynet.retpack(table.unpack(ret, 2))
end-- 初始化
local function init()
skynet.error(M.name .. " " .. M.id .. " init")
skynet.dispatch("lua", dispatch)
if M.init then
M.init()
end
end-- 启动服务
function M.start(name, id, ...)
M.name = name
M.id = tonumber(id)
skynet.start(init)
endreturn M
9、重写打工服务 有了
service
类,我们写服务的时候,只需要按下面这个模板写就可以了,local skynet = require "skynet"
local s = require "service"s.init = function()
-- 初始化
ends.resp.协议1 = function(source, ...)
-- TODO
ends.resp.协议2 = function(source, ...)
-- TODO
ends.start(...)
现在,我们重新写一下打工服务,改造后代码如下,我们后面新写服务也按照这个格式来写,统一,方便维护,
local skynet = require "skynet"
local s = require "service"s.money = 0
s.isworking = falses.update = function(frame)
if s.isworking then
s.money = s.money + 1
skynet.error(s.name .. tostring(s.id) .. ", money: " .. tostring(s.money))
end
ends.init = function ()
skynet.fork(s.timer)
ends.timer = function()
local stime = skynet.now()
local frame = 0
while true do
frame = frame + 1
local isok, err = pcall(s.update, frame)
if not isok then
skynet.error(err)
end
local etime = skynet.now()
local waittime = frame * 20 - (etime - stime)
if waittime <= 0 then
waittime = 2
end
skynet.sleep(waittime)
end
ends.resp.start_work = function(source)
s.isworking = true
ends.resp.stop_work = function(source)
s.isworking = false
ends.start(...)
10、买猫粮服务 打工挣钱,有了钱我们就可以买猫粮啦,我们来写一个买猫粮的服务脚本。
我们在
service
文件夹中新建一个buy
文件夹,如下,文章图片
进入
buy
目录,然后创建一个init.lua
脚本,文章图片
代码如下:
-- service/buy/init.lua脚本local skynet = require "skynet"
local s = require "service"s.cat_food_price = 5
s.cat_food_cnt = 0s.resp.buy = function (source)
-- 先扣费
local left_money = skynet.call("worker1", "lua", "change_money", -s.cat_food_price)
if left_money >= 0 then
s.cat_food_cnt = s.cat_food_cnt + 1
skynet.error("buy cat food ok, current cnt: " .. tostring(s.cat_food_cnt))
return true
end
-- 购买失败,把钱加回去
skynet.error("buy failed, money not enough")
skynet.call("worker1", "lua", "change_money", s.cat_food_price)
return false
ends.start(...)
然后我们给打工服务加多一个消息,
-- service/worker/init.lua脚本s.resp.change_money = function(source, delta)
s.money = s.money + delta
return s.money
end
最后,在主服务中加上买猫粮服务的启动和消息发送,如下:
local skynet = require "skynet"skynet.start(function ()
skynet.error("[start main] hello world")
-- 启动打工服务
local worker1 = skynet.newservice("worker", "worker", 1)
-- 启动买猫粮服务
local buy1 = skynet.newservice("buy", "buy", 1) -- 开始打工
skynet.send(worker1, "lua", "start_work")
skynet.sleep(200)
-- 结束打工
skynet.send(worker1, "lua", "stop_work")-- 买猫粮
skynet.send(buy1, "lua", "buy")-- 退出主服务
skynet.exit()
end)
好啦,现在我们测试一下,运行效果如下:
文章图片
可以看到,最后买猫粮成功啦~
文章图片
九、补充
1、网络模块 写服务端,肯定需要涉及到网络模块,需要用到
skynet.socket
,案例:-- main.lualocal skynet = require "skynet"
local socket = require "skynet.socket"local function on_connect(fd, addr)
socket.start(fd)
while true do
local readdata = https://www.it610.com/article/socket.read(fd)
if readdata then
-- TODO 处理消息-- 回应客户端,把readdata返回给客户端
-- socket.write(fd,"server get data: " .. readdata)
else
-- 连接断开了
socket.close(fd)
end
end
endskynet.start(function()
local listenfd = socket.listen("0.0.0.0", 8888)
socket.start(listenfd, on_connect)
end)
如果要写一个多人聊天功能的话,只需要把消息广播出去即可,例:
-- main.lualocal skynet = require "skynet"
local socket = require "skynet.socket"local clients = {
}local function on_connect(fd, addr)
clients[fd] = {
}
socket.start(fd)
while true do
local readdata = https://www.it610.com/article/socket.read(fd)
if readdata then
-- 广播
for client_fd, _ in pairs(clients) do
socket.write(client_fd, readdata)
end
else
-- 连接断开了
socket.close(fd)
clients[fd] = nil
end
end
endskynet.start(function()
local listenfd = socket.listen("0.0.0.0", 8888)
socket.start(listenfd, on_connect)
end)
2、节点集群 我上面写的打工赚钱买猫粮,都在同一个节点中,
文章图片
实际项目中,可能会开启多个节点,两个服务如果在同一个节点中,则通过
skynet.send
或skynet.call
来传递消息,注:send
是发送消息,不会阻塞调用方;call
是阻塞调用。
文章图片
如果在不同的节点中,则需要使用
cluster.send
或cluster.call
,注:send
是发送消息,不会阻塞调用方;call
是阻塞调用。
文章图片
对此,我们可以优化一下上文中的
service.lua
,封装send
和call
方法,-- lualib/service.lualocal cluster = require "skynet.cluster"...function M.call(node, srv, ...)
local mynode = skynet.getenv("node")
if node == mynode then
return skynet.call(srv, "lua", ...)
else
return cluster.call(node, srv, ...)
end
endfunction M.send(node, srv, ...)
local mynode = skynet.getenv("node")
if node == mynode then
return skynet.send(srv, "lua", ...)
else
return cluster.send(node, srv, ...)
end
end
我们在
etc/config.node1
的末尾加多一行node = "node1"
拷贝一份,重命名为
config.node2
,把末尾一行改为node = "node2"
然后,我们改下
main.lua
脚本,让它在node1
节点开启打工服务,在node2
节点开启买猫粮节点,最终main.lua
脚本如下:local skynet = require "skynet"
local cluster = require "skynet.cluster"
require "skynet.manager"skynet.start(function ()
skynet.error("[start main] hello world") -- 集群配置
cluster.reload({node1 = "127.0.0.1:7001",
node2 = "127.0.0.1:7002",
})local mynode = skynet.getenv("node")
if "node1" == mynode then
-- 启动集群节点
cluster.open("node1")
-- node1节点,开启打工服务
local worker1 = skynet.newservice("worker", "worker", 1)
skynet.name("worker1", worker1)
skynet.send(worker1, "lua", "start_work")
skynet.sleep(200)
skynet.send(worker1, "lua", "stop_work")
elseif "node2" == mynode then
-- 启动集群节点
cluster.open("node2")
-- node2节点,开启买猫粮服务
local buy1 = skynet.newservice("buy", "buy", 1)
-- 请求买猫粮,买三次
skynet.send(buy1, "lua", "buy")
skynet.send(buy1, "lua", "buy")
skynet.send(buy1, "lua", "buy")
endskynet.exit()
end)
最后,我们改下
buy/init.lua
脚本,把skynet.call
改成s.call
,如下:local skynet = require "skynet"
local s = require "service"s.cat_food_price = 5
s.cat_food_cnt = 0s.resp.buy = function (source)local left_money = s.call("node1", "worker1", "change_money", -s.cat_food_price)if left_money >= 0 then
s.cat_food_cnt = s.cat_food_cnt + 1
skynet.error("buy cat food ok, current cnt: " .. tostring(s.cat_food_cnt))
return true
end
skynet.error("buy cat food failed, money not enough")
s.call("node1", "worker1", "change_money", s.cat_food_price)
return false
ends.start(...)
好了,现在我们先开启
节点1
,在终端执行命令./skynet/skynet etc/config.node1
可以看到
节点1
开启了打工服务,赚了10块钱
,文章图片
现在我们开启
节点2
,在终端执行命令./skynet/skynet etc/config.node2
因为猫粮价格是
5块钱
一包,所以只能买两次,执行结果如下,可以看到第三次买猫粮失败,因为钱不够了,文章图片
3、数据库模块 3.1、安装MySQL 在终端执行命令
sudo apt-get install mysql-server
执行过程中会弹出框让你输入
MySQL
的root
账号的密码,如下,需要输入两次,文章图片
安装完毕后,执行
mysql --version
,如果输出版本号,则说明安装成功了,文章图片
3.2、启动MySQL 在终端执行命令
service mysql start
此时会弹出一个框,注意此处输入
Ubuntu
的开机密码,而不是MySQL
的root
账号密码哦,点击Authenticate
,文章图片
启动成功后,我们可以通过下面的命令看是否有
mysql
的进程,ps -axj |grep mysql
可以看到有
mysql
进程,说明MySQL
服务已经成功启动了,文章图片
3.3、关闭MySQL 在终端执行命令
service mysql stop
此时会弹出一个框,此处输入
Ubuntu
的开机密码,点击Authenticate
,文章图片
执行完毕后,同理,我们可以通过下面的命令查看是否已经没有
mysql
进程了,ps -axj |grep mysql
3.4、登录MySQL 在终端执行命令
mysql -h127.0.0.1 -uroot -p你的密码
如下,登录成功,
文章图片
现在我们可以愉快地使用
mysql
啦,执行show databases;
,查看所有的数据库文章图片
3.5、在skynet中操作数据库 我们先在终端手动创建一个数据库,
create database test_db;
如下,
文章图片
选择
test_db
,文章图片
接着再创建一个表,
create table users (
id int not null auto_increment,
name varchar(30) not null,
primary key (id));
如下:
文章图片
好了,现在我们在
skynet
中来操作数据库吧,例:local skynet = require "skynet"
local mysql = require "skynet.db.mysql"skynet.start(function ()
skynet.error("[start main] hello world")local db = mysql.connect(
{host = "127.0.0.1",
port = 3306,
database = "test_db",
user = "root",
password = "123456",
max_packet_size = 1024 * 1024,
on_connect = nil
}
)
-- 插入数据
local res = db:query("insert into users(name) values (\'linxinfa\')")
-- 查询数据
res = db:query('select * from users')
for i, v in pairs(res) do
print(i, " " .. v.id .. " " .. v.name)
endskynet.exit()
end)
我们执行两次,结果如下,可以看到,数据正常写入到数据库中了,成功~
文章图片
十、完毕
好了,就先写这么多吧,关于
skynet
还有很多很多内容,本文只是一个入门,希望可以帮助到新手同学~我是林新发:https://blog.csdn.net/linxinfa
原创不易,若转载请注明出处,感谢大家~
喜欢我的可以点赞、关注、收藏,如果有什么技术上的疑问,欢迎留言或私信~
推荐阅读
- 宽容谁
- 我要做大厨
- 增长黑客的海盗法则
- 画画吗()
- 2019-02-13——今天谈梦想()
- 远去的风筝
- 三十年后的广场舞大爷
- 叙述作文
- 20190302|20190302 复盘翻盘
- 学无止境,人生还很长