构建代码
在建立了底层通信之后,只需要写disp_serv上层业务逻辑即可,我们在sgame/servers/目录下新建一个disp_serv和disp_serv/lib
- 目录如下:
|-- disp_serv
| `-- lib
- 拷贝connect_serv目录下的connect_serv.go到disp_serv/并改名为disp_serv.go。 拷贝connect_serv/lib/目录下的base.go,hearbeat.go,recv_msg.go,report.go,send_msg.go 这几个文件到相应位置,如下所示:
|-- disp_serv
| |-- disp_serv.go
| `-- lib
| |-- base.go
| |-- hearbeat.go
| |-- recv_msg.go
| |-- report.go
| `-- send_msg.go
上面这几个文件就构成了一个进程的基本功能。主要代码都可复用,只需要修改几处个性化的地方即可。下面依次来看
基础代码
- disp_serv/disp_serv.go 这是进程的主函数,主要调用lib里的各API完成,主要是解析参数;通用设置;本地设置以及拉起进程。因为参数都是统一的,这里代码基本不用修改
func parse_flag() bool {
//check flag
flag.Parse()
if len(*name_space) <= 0 || *proc_id <= 0 || len(*config_file) <= 0 || len(*proc_name) <= 0 {
flag.PrintDefaults()
return false
}
pconfig.ProcId = *proc_id
pconfig.NameSpace = *name_space
pconfig.ConfigFile = *config_file
pconfig.ProcName = *proc_name
pconfig.Daemon = *daemonize
return true
}
func main() {
//parse flag
if parse_flag() == false {
return
}
//comm set
if lib.CommSet(pconfig) == false {
fmt.Printf("comm set failed!\n")
return
}
//local set
if lib.LocalSet(pconfig) == false {
fmt.Printf("self set failed!\n")
return
}
//start server
lib.ServerStart(pconfig)
}
- lib/base.go 这个文件包含了进程的主要实现和配置的建立,所以是非常重要,但主要代码仍然可以复用,只需要修改少数几个地方。
type FileConfig struct {
LogicServList []int `json:"logic_serv_list"`
LogFile string `json:"log_file"`
ManageAddr []string `json:"manage_addr"`
MonitorInv int `json:"monitor_inv"` //monitor interval seconds
}
type Config struct {
//comm
NameSpace string
ProcId int
ProcName string
ConfigFile string
Daemon bool
FileConfig *FileConfig
Comm *comm.CommConfig
ReportCmd string //used for report cmd
ReportCmdToken int64
ReportServ *comm.ReportServ //report to manger
//local
}
主要的个性化部分就是FileConfig的配置,这里是对应配置文件的,即后面我们需要在spush/tmpl/里配置的disp_serv.tmpl,只要这里对应上,其他的逻辑基本不用修改。只需要注意Config里local后的部分,但disp_serv没啥local的 所以无需改动,这里简单说下base.go各主要函数用途
func CommSet(pconfig *Config) bool
这个是通用的设置,一般无需改动func LocalSet(pconfig *Config) bool
这个是本地化的一些设置。比如connect_serv需要加tcp_serv;db_serv需要加redis_client等,但我们这里用不到,就是一些通用的比如加上定时器等功能,基本复用即可func ServerExit(pconfig *Config)
这是进程退出时的执行代码,一般复用即可,除非进程有特别的自定义设置需要关闭
func ServerStart(pconfig *Config) {
var log = pconfig.Comm.Log
var default_sleep = time.Duration(comm.DEFAULT_SERVER_SLEEP_IDLE)
log.Info("%s starts---%v", pconfig.ProcName, os.Args)
//each support routine
go comm.HandleSignal(pconfig.Comm)
//main loop
for {
//handle info
handle_info(pconfig)
//recv pkg
RecvMsg(pconfig)
//tick
handle_tick(pconfig)
//sleep
time.Sleep(time.Millisecond * default_sleep)
}
}
这是进程的主要结构,包括拉起信号处理协程;处理不同的内部信息;接收进程包;处理定时等 一般不做改动
-
func AfterReLoadConfig(pconfig *Config, old_config *FileConfig, new_config *FileConfig)
这个是各进程自己处理的在运行中重新加载配置时的个性化处理函数,自己按需添加内容 -
func handle_info(pconfig *Config)
进程内部的一些模块通信,比如停机,重新加载配置,开始性能检测等 一般直接拷贝即可 -
func handle_tick(pconfig *Config)
触发定时器的 直接使用 -
lib/hearbeat.go 进程定期向对端发送心跳包,目前包括以下几个函数:
func SendHeartBeatMsg(arg interface{})
向对端发送心跳包,这里需要修改函数里对端的目标地址即可,比如我们这里是向所有logic_serv发心跳包:
...
//send msg
for _ , logic_id := range pconfig.FileConfig.LogicServList {
ret := proc.Send(logic_id, buff, len(buff));
if ret < 0 {
lp.Err("send msg to %d failed! err:%d", logic_id, ret);
}
}
...
-
func RecvHeartBeatReq(pconfig *Config , preq *ss.MsgHeartBeatReq , from int)
收到对端心跳包的更新 一般不用特殊改 -
func ReportSyncServer(arg interface{})
向manage定时同步进程信息 -
lib/recv_msg.go 通过通信组件收到其他进程发来的协议包,这里是服务器间通信协议分发的场所,在新增协议处理时需要改动,主要函数为:
func RecvMsg(pconfig *Config) int64
-
lib/report.go : 向manage上报以及对manage的命令处理,后续根据需求添加即可 新进程直接用
-
lib/send_msg.go : 用于向其他进程发送协议包的汇聚文件,根据后续扩展,目前只有
func SendToLogic(pconfig *Config , logic_serv int , buff []byte) bool
发送到某个logic_serv进程 -
在代码编写(拷贝)完成之后,我们进入disp_serv/; go build disp_serv.go 编译通过即会生成disp_serv。然后进入发布
发布
- 发布配置
进入sgame/servers/spush目录,打开sgame.json 新增disp_serv,如下所示:
{
"task":"sgame" ,
"deploy_host":"" ,
"deploy_timeout":60,
"remote_user":"nmsoccer" ,
"remote_pass":"****" ,
"procs":[
{"name":"conn_serv-1" , "bin":["../conn_serv/conn_serv"] , "host":"127.0.0.1" , "host_dir":"/home/nmsoccer/sg/group1/conn_serv/" , "cmd":"./conn_serv -N sgame -p 10001 -P conn_serv-1 -f conf/conn_serv.json -D"},
...
{"name":"disp_serv-1" , "bin":["../disp_serv/disp_serv"] , "host":"" , "host_dir":"/home/nmsoccer/sg/disp_serv/disp_serv-1/" , "cmd":"./disp_serv -N sgame -p 40001 -P disp_serv-1 -f conf/disp_serv.json -D"},
{"name":"disp_serv-2" , "bin":["../disp_serv/disp_serv"] , "host":"" , "host_dir":"/home/nmsoccer/sg/disp_serv/disp_serv-2/" , "cmd":"./disp_serv -N sgame -p 40002 -P disp_serv-1 -f conf/disp_serv.json -D"},
{"name":"manage_serv-1" , "bin":["../manage_serv/manage_serv" , "../manage_serv/html_tmpl/"] , "host":"" , "host_dir":"/home/nmsoccer/sg/manage/manage_serv-1/" , "cmd":"./manage_serv -N sgame -P manage_serv-1 -f conf/manage_serv.json -D"}
],
"proc_cfgs":[
{"name":"conn_serv-1" , "cfg_name":"conf/conn_serv.json" , "cfg_tmpl":"./tmpl/conn_serv.tmpl" , "tmpl_param":"logic_serv=20001,listen_addr=:18909"},
...
{"name":"disp_serv-1" , "cfg_name":"conf/disp_serv.json" , "cfg_tmpl":"./tmpl/disp_serv.tmpl" , "tmpl_param":""},
{"name":"disp_serv-2" , "cfg_name":"conf/disp_serv.json" , "cfg_tmpl":"./tmpl/disp_serv.tmpl" , "tmpl_param":""},
{"name":"manage_serv-1" , "cfg_name":"conf/manage_serv.json" , "cfg_tmpl":"./tmpl/manage_serv.tmpl" , "tmpl_param":"listen_addr=:7000,http_addr=:8080"}
]
}
其中两个disp_serv是我们新增的,我们将分别将其部署到/home/nmsoccer/sg/disp_serv/disp_serv-1/与/home/nmsoccer/sg/disp_serv/disp_serv-2/目录里,并分别用 ./disp_serv -N sgame -p 40001 -P disp_serv-1 -f conf/disp_serv.json -D
和 ./disp_serv -N sgame -p 40002 -P disp_serv-1 -f conf/disp_serv.json -D
将它们拉起。 注意看这里的参数40001,40002是之前配置bridge.cfg里分配给它们的proc_id,同时参数里带的进程名尽量保持一致,虽然两者没毛的关系
- 进程配置
我们看到proc_cfg里定义了conf/disp_serv.json,这是未来将自动生成的进程配置文件,它们主要依据是tmpl/disp_serv.tmpl生成,我们可以打开看下:
cat tmpl/disp_serv.tmpl:
{
"logic_serv_list":[20001,20002],
"log_file":"disp_serv.log",
"manage_addr":[":7000" , "127.0.0.1:7001"],
"monitor_inv":5
}
这个文件需要对应之前的lib/base.go里的FileConfig:
type FileConfig struct {
LogicServList []int `json:"logic_serv_list"`
LogFile string `json:"log_file"`
ManageAddr []string `json:"manage_addr"`
MonitorInv int `json:"monitor_inv"` //monitor interval seconds
}
这里就是使用GO的json解析,比较方便
- 发布 然后我们就可以使用spush工具发布disp_serv了:
./spush -p ^disp_ser* -f sgame.json
++++++++++++++++++++spush (2020-08-11 21:28:17)++++++++++++++++++++
.push some procs:^disp_ser*
matched procs num:2
create cfg:9/9
----------Push <sgame> Result----------
ok
.
[2/2]
[disp_serv-1]::success
[disp_serv-2]::success
+++++++++++++++++++++end (2020-08-11 21:28:19)+++++++++++++++++++++
- 修改管理配置 我们要将disp_serv加到页面管理,只需要打开spush/tmpl/manage_serv.tmpl
{
"listen_addr":"$listen_addr",
"http_addr":"$http_addr",
"log_file":"manage_serv.log",
"client_list":[[10001,"conn_serv-1"],[20001,"logic_serv-1"],[30001,"db_logic_serv-1"],[10002,"conn_serv-2"],[20002,"logic_serv-2"],[30002,"db_logic_serv-2"],[40001,"disp_serv-1"],[40002,"disp_serv-2"]],
"heart_timeout":20,
"reload_timeout":10,
"auth":["xxx" , "ooo"],
"auth_expire":3600
}
新加[40001,"disp_serv-1"],[40002,"disp_serv-2"]项目,然后生成并推送mange配置
./spush -p man* -f sgame.json -O
++++++++++++++++++++spush (2020-08-11 21:31:53)++++++++++++++++++++
push some procs:man*
.matched procs num:1
create cfg:9/9
----------Push <sgame> Result----------
ok
.
[1/1]
[manage_serv-1]::success
+++++++++++++++++++++end (2020-08-11 21:31:54)+++++++++++++++++++++
然后kill -10 manage_serv[pid]让其重新加载配置,log如下:
[2020-08-11 21:33:02.477 info] recv signal usr1!
[2020-08-11 21:33:02.481 info] >>reload config!
[2020-08-11 21:33:02.481 info] <LoadJsonFile> load conf/manage_serv.json success!config:&{:7000 :8080 manage_serv.log [[10001 conn_serv-1] [20001 logic_serv-1] [30001 db_logic_serv-1] [10002 conn_serv-2] [20002 logic_serv-2] [30002 db_logic_serv-2] [40001 disp_serv-1] [40002 disp_serv-2]] 20 10 [xxx ooo] 3600}
[2020-08-11 21:33:02.481 info] <AfterReLoadConfig> finish
可以看到加载成功啦 ,然后点开页面127.0.0.1:8080/index 就可以看到新的disp_serv监控。具体的管理端请参见页面baba