方便展示项目结构,我将代码例子都放在skynet-guide仓库。
将skynet和skynet-guide放到统一目录下。
$ git clone https://github.com/cloudwu/skynet
$ git clone https://github.com/kasicass/skynet-guide
编译好skynet,之后:
$ cd skynet-guide/02-life-cycle
$ ./tools/bootstrap.sh
bootstrap会从skynet目录复制所需要的文件。
正式项目,会规划好目录结构,一眼便知道,哪些是外部依赖库、哪些是项目自身的代码和资源。02-life-cycle演示了一个正式项目的组织结构。
$ tree -L 2
.
├── conf
│ ├── server.conf # 启动配置
│ └── server-deamon.conf
├── engine # 引擎目录(从skynet复制过来的文件)
│ ├── cservice
│ ├── luaclib
│ ├── lualib
│ └── service
├── server # 服务端代码目录
│ └── service
│ ├── cat.lua
│ └── main.lua
├── skynet # skynet binary
└── tools # 初始化、启动脚本
├── bootstrap.sh
└── cleanup.sh
skynet启动时,需要传入一个配置文件(一个lua脚本)。
cpath = "./engine/cservice/?.so"
lua_cpath = "./engine/luaclib/?.so"
lualoader = "./engine/lualib/loader.lua"
lua_path = "./engine/lualib/?.lua;./server/lualib/?.lua"
luaservice = "./engine/service/?.lua;./server/service/?.lua"
snax = "./server/?.lua"
thread = 8
logger = nil
logpath = "."
harbor = 1
address = "127.0.0.1:2526"
master = "127.0.0.1:2013"
start = "main" # script/service/main.lua
bootstrap = "snlua bootstrap" # engine/service/bootstrap.lua
standalone = "0.0.0.0:2013"
lua service的启动流程:
bootstrap 表示引擎启动后,启动的第一个 lua service;然后 bootstrap 中,启动游戏的第一个 service "main"
start = "main"
bootstrap = "snlua bootstrap"
看
$ ./skynet conf/game.conf
[:01000002] LAUNCH snlua bootstrap
...
[:01000009] LAUNCH snlua main
[:0100000a] LAUNCH snlua cat
[:0100000a] CAT: miao~ miao~
[:01000009] KILL self
[:01000002] KILL self
[:0100000a] CAT: bye~
[:0100000a] KILL self
在 main.lua 中,再启动 "cat"。
local skynet = require "skynet"
skynet.start(function()
skynet.newservice("cat")
skynet.exit()
end)
cat.lua 中,skynet.fork() 启动另一个 coroutine,跑 cat service 具体的业务
local skynet = require "skynet.manager"
local function cat_main_loop()
skynet.error("CAT: miao~ miao~")
skynet.sleep(300) -- 3s
skynet.error("CAT: bye~")
skynet.abort()
end
skynet.start(function()
skynet.fork(cat_main_loop)
end)
看 cat.lua 中
local skynet = require "skynet.manager"
local function cat_main_loop()
...
skynet.abort()
end
每个 service 都是独立的 lua vm,需要独立 require 所需要的功能
skynet.abort()
│
▼
cmd_abort() → skynet_handle_retireall()
│ │
│ ├─ retire 服务1 → context_release → delete_context → total--
│ ├─ retire 服务2 → context_release → delete_context → total--
│ └─ ... 直到所有 slot 为空
│
▼
total == 0
│
├─ timer 线程: CHECK_ABORT → break
│ ├─ skynet_socket_exit() → socket 线程退出
│ └─ m->quit=1, broadcast → worker 线程退出
│
├─ monitor 线程: CHECK_ABORT → break
│
▼
pthread_join (所有线程)
│
▼
skynet_harbor_exit()
skynet_socket_free()
daemon_exit() ← 删除 pid 文件
│
▼
进程退出
在生成环境,都会以 daemon 方式运行服务端进程。对于 skynet,只需要修改下配置文件:
logger = "./skynet.log"
daemon = "./skynet.pid"
看
$ ./skynet conf/server-deamon.conf
$ cat skynet.pid
2827277
$ cat skynet.log
04/06/26 15:32:11.31 [:01000002] LAUNCH snlua bootstrap
04/06/26 15:32:11.32 [:01000003] LAUNCH snlua launcher
04/06/26 15:32:11.32 [:01000004] LAUNCH snlua cmaster
04/06/26 15:32:11.33 [:01000004] master listen socket 0.0.0.0:2013
04/06/26 15:32:11.33 [:01000005] LAUNCH snlua cslave
04/06/26 15:32:11.33 [:01000005] slave connect to master 127.0.0.1:2013
04/06/26 15:32:11.33 [:01000004] connect from 127.0.0.1:41374 4
04/06/26 15:32:11.33 [:01000006] LAUNCH harbor 1 16777221
04/06/26 15:32:11.33 [:01000004] Harbor 1 (fd=4) report 127.0.0.1:2526
04/06/26 15:32:11.33 [:01000005] Waiting for 0 harbors
04/06/26 15:32:11.33 [:01000005] Shakehand ready
04/06/26 15:32:11.33 [:01000007] LAUNCH snlua datacenterd
04/06/26 15:32:11.34 [:01000008] LAUNCH snlua service_mgr
04/06/26 15:32:11.34 [:01000009] LAUNCH snlua main
04/06/26 15:32:11.34 [:0100000a] LAUNCH snlua cat
04/06/26 15:32:11.34 [:0100000a] CAT: miao~ miao~
04/06/26 15:32:11.34 [:01000009] KILL self
04/06/26 15:32:11.34 [:01000002] KILL self