2021-03-28 16:04:42 +08:00
|
|
|
// Package websocket...
|
|
|
|
//
|
|
|
|
// Description : websocket...
|
|
|
|
//
|
2021-06-05 22:13:09 +08:00
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
2021-03-28 16:04:42 +08:00
|
|
|
//
|
|
|
|
// Date : 2021-03-27 6:49 下午
|
|
|
|
package websocket
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
2021-04-19 00:03:25 +08:00
|
|
|
"github.com/gin-contrib/pprof"
|
|
|
|
|
2022-06-27 15:50:41 +08:00
|
|
|
"git.zhangdeman.cn/zhangdeman/logger"
|
2021-04-17 21:41:40 +08:00
|
|
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
2021-11-17 18:30:49 +08:00
|
|
|
"git.zhangdeman.cn/zhangdeman/websocket/message"
|
2021-04-17 15:04:43 +08:00
|
|
|
|
2021-11-17 18:30:49 +08:00
|
|
|
"git.zhangdeman.cn/zhangdeman/websocket/config"
|
2021-04-17 15:04:43 +08:00
|
|
|
|
2021-04-09 16:08:00 +08:00
|
|
|
"github.com/tidwall/gjson"
|
|
|
|
|
2021-11-17 18:30:49 +08:00
|
|
|
"git.zhangdeman.cn/zhangdeman/websocket/context"
|
2021-03-28 16:04:42 +08:00
|
|
|
|
|
|
|
"gopkg.in/olahol/melody.v1"
|
|
|
|
|
2021-11-17 18:30:49 +08:00
|
|
|
"git.zhangdeman.cn/zhangdeman/websocket/abstract"
|
2021-03-28 16:04:42 +08:00
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Server ...
|
|
|
|
//
|
2021-06-05 22:13:09 +08:00
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
2021-03-28 16:04:42 +08:00
|
|
|
//
|
|
|
|
// Date : 8:04 下午 2021/3/27
|
|
|
|
type Server struct {
|
2021-04-18 21:55:41 +08:00
|
|
|
ginRouter *gin.Engine // GIN引擎
|
|
|
|
wsServer *melody.Melody // websocket引擎
|
|
|
|
conf *config.WSServerConfig // 配置
|
2021-03-28 16:04:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ginRouterTable 表
|
2021-04-18 00:43:19 +08:00
|
|
|
ginRouterTable map[int]*gin.Engine
|
|
|
|
// wsServerTable 表
|
|
|
|
wsServerTable map[int]map[string]*Server
|
2021-03-28 16:04:42 +08:00
|
|
|
// commandTable 指令表
|
|
|
|
commandTable map[string]map[string]abstract.ICommand
|
2021-04-18 21:55:41 +08:00
|
|
|
// 日志实例表
|
|
|
|
loggerInstanceTable map[string]*zap.Logger
|
2021-03-28 16:04:42 +08:00
|
|
|
// 服务启停的信号
|
2021-11-17 18:36:57 +08:00
|
|
|
sigChan = make(chan int, 1)
|
2021-03-28 16:04:42 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// NewWebsocketServe 启动websocket服务
|
|
|
|
//
|
2021-06-05 22:13:09 +08:00
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
2021-03-28 16:04:42 +08:00
|
|
|
//
|
|
|
|
// Date : 6:49 下午 2021/3/27
|
2021-11-17 18:54:34 +08:00
|
|
|
func NewWebsocketServe(finishHook func(), wsInstanceList ...abstract.IWebsocket) error {
|
2021-03-28 16:04:42 +08:00
|
|
|
if len(wsInstanceList) == 0 {
|
|
|
|
return errors.WithStack(errors.New("register websocket server list is empty"))
|
|
|
|
}
|
2021-04-18 00:43:19 +08:00
|
|
|
ginRouterTable = make(map[int]*gin.Engine)
|
|
|
|
wsServerTable = make(map[int]map[string]*Server)
|
2021-03-28 16:04:42 +08:00
|
|
|
commandTable = make(map[string]map[string]abstract.ICommand)
|
|
|
|
for _, wsInstance := range wsInstanceList {
|
2021-04-18 00:43:19 +08:00
|
|
|
initServer(wsInstance)
|
|
|
|
}
|
2021-11-17 18:54:34 +08:00
|
|
|
run(finishHook)
|
2021-04-18 00:43:19 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 初始化server
|
|
|
|
func initServer(wsInstance abstract.IWebsocket) {
|
|
|
|
// 初始化ws server
|
|
|
|
// 初始化 gin 路由表
|
|
|
|
if _, exist := ginRouterTable[wsInstance.GetServerPort()]; !exist {
|
|
|
|
ginRouterTable[wsInstance.GetServerPort()] = gin.Default()
|
|
|
|
}
|
|
|
|
// 初始化WS-Server表
|
|
|
|
if _, exist := wsServerTable[wsInstance.GetServerPort()]; !exist {
|
|
|
|
wsServerTable[wsInstance.GetServerPort()] = make(map[string]*Server)
|
|
|
|
}
|
2021-04-18 21:55:41 +08:00
|
|
|
// 初始化日志实例表
|
|
|
|
loggerInstanceTable = make(map[string]*zap.Logger)
|
2021-04-18 00:43:19 +08:00
|
|
|
if _, exist := wsServerTable[wsInstance.GetServerPort()][wsInstance.GetModuleFlag()]; !exist {
|
|
|
|
// 生成并存储 WS-Server
|
|
|
|
wsSetConfigList := wsInstance.GetWSServerConfig()
|
|
|
|
if nil == wsSetConfigList {
|
|
|
|
wsSetConfigList = make([]config.SetWSServerConfig, 0)
|
|
|
|
}
|
|
|
|
s := &Server{
|
|
|
|
ginRouter: gin.Default(),
|
|
|
|
wsServer: melody.New(),
|
|
|
|
conf: config.NewWSServerConfig(wsSetConfigList...),
|
|
|
|
}
|
|
|
|
if s.conf.LogEnable {
|
|
|
|
// 开启了日志,初始化日志
|
|
|
|
if len(s.conf.LogPath) == 0 {
|
|
|
|
panic(wsInstance.GetModuleFlag() + " 模块开启了日志记录,但是没有配置日志路径")
|
|
|
|
}
|
|
|
|
var (
|
|
|
|
err error
|
|
|
|
logConf *logger.RotateLogConfig
|
|
|
|
)
|
|
|
|
optionList := make([]logger.SetLoggerOptionFunc, 0)
|
|
|
|
if s.conf.LogConsole {
|
|
|
|
optionList = append(optionList, logger.WithConsoleOutput())
|
|
|
|
}
|
|
|
|
if s.conf.LogConsole {
|
|
|
|
optionList = append(optionList, logger.WithConsoleOutput())
|
|
|
|
}
|
|
|
|
if len(s.conf.LogFile) == 0 {
|
|
|
|
s.conf.LogFile = wsInstance.GetModuleFlag() + ".log"
|
2021-03-28 16:04:42 +08:00
|
|
|
}
|
2021-04-18 00:43:19 +08:00
|
|
|
if logConf, err = logger.NewRotateLogConfig(s.conf.LogPath, s.conf.LogFile, logger.WithTimeIntervalType(s.conf.LogSplitInterval)); nil != err {
|
|
|
|
panic(wsInstance.GetModuleFlag() + " 模块开启了日志记录,日志初始化失败, 失败原因 : " + err.Error())
|
2021-03-28 16:04:42 +08:00
|
|
|
}
|
2021-04-18 21:55:41 +08:00
|
|
|
if loggerInstance, err := logger.NewLogger(s.conf.LogLevel, logConf, optionList...); nil != err {
|
2021-04-18 00:43:19 +08:00
|
|
|
panic(wsInstance.GetModuleFlag() + " 模块开启了日志记录,日志初始化失败, 失败原因 : " + err.Error())
|
2021-04-18 21:55:41 +08:00
|
|
|
} else {
|
|
|
|
loggerInstanceTable[wsInstance.GetModuleFlag()] = loggerInstance
|
2021-03-28 16:04:42 +08:00
|
|
|
}
|
2021-04-18 00:43:19 +08:00
|
|
|
}
|
2021-04-18 21:55:41 +08:00
|
|
|
if nil == loggerInstanceTable[wsInstance.GetModuleFlag()] && s.conf.LogConsole {
|
2021-04-18 00:43:19 +08:00
|
|
|
// 没有配置文件日志, 但是配置了控制台输出
|
2021-04-18 21:55:41 +08:00
|
|
|
if loggerInstance, err := logger.NewConsoleLogger(s.conf.LogLevel); nil != err {
|
2021-04-18 00:43:19 +08:00
|
|
|
panic(wsInstance.GetModuleFlag() + " 模块开启了控制台日志记录,日志初始化失败, 失败原因 : " + err.Error())
|
2021-04-18 21:55:41 +08:00
|
|
|
} else {
|
|
|
|
loggerInstanceTable[wsInstance.GetModuleFlag()] = loggerInstance
|
2021-03-28 16:04:42 +08:00
|
|
|
}
|
2021-04-18 00:43:19 +08:00
|
|
|
}
|
2021-04-22 22:03:17 +08:00
|
|
|
// 对长连接进行配置
|
|
|
|
s.wsServer.Config.MaxMessageSize = s.conf.MaxMessageSize
|
|
|
|
s.wsServer.Config.MessageBufferSize = s.conf.MessageBufferSize
|
|
|
|
s.wsServer.Config.WriteWait = s.conf.WriteWait
|
|
|
|
s.wsServer.Config.PongWait = s.conf.PongWait
|
|
|
|
s.wsServer.Config.PingPeriod = s.conf.PingPeriod
|
|
|
|
|
2021-04-18 00:43:19 +08:00
|
|
|
wsServerTable[wsInstance.GetServerPort()][wsInstance.GetModuleFlag()] = s
|
2021-03-28 16:04:42 +08:00
|
|
|
}
|
2021-04-18 00:43:19 +08:00
|
|
|
|
|
|
|
// 初始化指令存储表
|
|
|
|
if _, exist := commandTable[wsInstance.GetModuleFlag()]; !exist {
|
|
|
|
commandTable[wsInstance.GetModuleFlag()] = make(map[string]abstract.ICommand)
|
|
|
|
}
|
|
|
|
routerGroup := ginRouterTable[wsInstance.GetServerPort()].Group(wsInstance.GetModuleFlag())
|
|
|
|
// 注册路由
|
|
|
|
for _, path := range wsInstance.HandshakeURL() {
|
|
|
|
routerGroup.GET(path, func(ctx *gin.Context) {
|
2021-04-18 21:55:41 +08:00
|
|
|
wsCtx := context.NewContext(ctx, wsInstance.GetModuleFlag(), nil)
|
2021-04-18 00:43:19 +08:00
|
|
|
parameter := map[string]interface{}{
|
2021-04-18 21:55:41 +08:00
|
|
|
"ws_context": wsCtx,
|
|
|
|
}
|
|
|
|
if err := wsServerTable[wsInstance.GetServerPort()][wsInstance.GetModuleFlag()].wsServer.HandleRequestWithKeys(ctx.Writer, ctx.Request, parameter); nil != err {
|
|
|
|
log(
|
|
|
|
loggerInstanceTable[wsInstance.GetModuleFlag()],
|
|
|
|
logFuncPanic,
|
|
|
|
"模块启动,注册路由,绑定数据失败",
|
|
|
|
getLoadDataList(wsCtx, zap.Error(err)),
|
|
|
|
)
|
2021-04-18 00:43:19 +08:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2021-04-18 21:55:41 +08:00
|
|
|
currentWSServer := getWsServer(wsInstance.GetServerPort(), wsInstance.GetModuleFlag())
|
2021-04-19 00:03:25 +08:00
|
|
|
// 注册pprof
|
|
|
|
if currentWSServer.conf.EnablePprof {
|
|
|
|
pprofGinRouter, exist := ginRouterTable[currentWSServer.conf.PprofPort]
|
|
|
|
if !exist {
|
|
|
|
pprofGinRouter = gin.Default()
|
|
|
|
ginRouterTable[currentWSServer.conf.PprofPort] = pprofGinRouter
|
|
|
|
}
|
|
|
|
pprof.Register(pprofGinRouter)
|
|
|
|
}
|
2021-04-18 00:43:19 +08:00
|
|
|
// 注册回调函数
|
2021-04-18 21:55:41 +08:00
|
|
|
// 1. 建立连接的函数注册回调函数
|
|
|
|
// //
|
|
|
|
currentWSServer.wsServer.HandleConnect(func(session *melody.Session) {
|
2021-04-18 00:43:19 +08:00
|
|
|
ctxInterface, _ := session.Get("ws_context")
|
|
|
|
ctx := ctxInterface.(*context.WSContext)
|
|
|
|
ctx.Session = session
|
|
|
|
if err := wsInstance.Connect(ctx); nil == err {
|
2021-04-18 21:55:41 +08:00
|
|
|
if currentWSServer.conf.StoreConnection && nil != currentWSServer.conf.ConnectionManager {
|
|
|
|
currentWSServer.conf.ConnectionManager.Store(ctx)
|
2021-04-18 00:43:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
// 2. 指令处理的函数
|
2021-04-18 21:55:41 +08:00
|
|
|
currentWSServer.wsServer.HandleMessage(func(session *melody.Session, bytes []byte) {
|
2021-04-18 00:43:19 +08:00
|
|
|
// TODO : 增加指令回调失败的callback
|
|
|
|
ctxInterface, _ := session.Get("ws_context")
|
|
|
|
ctx := ctxInterface.(*context.WSContext)
|
2021-04-18 20:11:48 +08:00
|
|
|
_ = dispatchCommand(context.CloneContext(ctx), bytes)
|
2021-04-18 00:43:19 +08:00
|
|
|
})
|
|
|
|
// 3, 关闭连接的处理函数
|
2021-04-18 21:55:41 +08:00
|
|
|
currentWSServer.wsServer.HandleClose(func(session *melody.Session, i int, s string) error {
|
2021-04-18 00:43:19 +08:00
|
|
|
ctxInterface, _ := session.Get("ws_context")
|
|
|
|
ctx := ctxInterface.(*context.WSContext)
|
|
|
|
defer func() {
|
|
|
|
if !wsServerTable[wsInstance.GetServerPort()][wsInstance.GetModuleFlag()].conf.StoreConnection || nil == wsServerTable[wsInstance.GetServerPort()][wsInstance.GetModuleFlag()].conf.ConnectionManager {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
wsServerTable[wsInstance.GetServerPort()][wsInstance.GetModuleFlag()].conf.ConnectionManager.Del(ctx, "")
|
|
|
|
}()
|
|
|
|
return wsInstance.Close(ctx, i, s)
|
|
|
|
})
|
|
|
|
// 4. 断开连接的处理函数
|
2021-04-18 21:55:41 +08:00
|
|
|
currentWSServer.wsServer.HandleDisconnect(func(session *melody.Session) {
|
2021-04-18 00:43:19 +08:00
|
|
|
ctxInterface, _ := session.Get("ws_context")
|
|
|
|
ctx := ctxInterface.(*context.WSContext)
|
|
|
|
defer func() {
|
2021-04-18 21:55:41 +08:00
|
|
|
if nil == currentWSServer.conf.ConnectionManager {
|
2021-04-18 00:43:19 +08:00
|
|
|
return
|
|
|
|
}
|
2021-04-18 21:55:41 +08:00
|
|
|
currentWSServer.conf.ConnectionManager.Del(ctx, "")
|
2021-04-18 00:43:19 +08:00
|
|
|
}()
|
|
|
|
wsInstance.Disconnect(ctx)
|
|
|
|
})
|
|
|
|
// 注册指令
|
|
|
|
for _, cmd := range wsInstance.GetCommandList() {
|
2021-04-18 21:55:41 +08:00
|
|
|
log(
|
|
|
|
getLoggerInstance(wsInstance.GetModuleFlag(), nil),
|
|
|
|
logFuncInfo,
|
|
|
|
"长连接指令注册成功",
|
|
|
|
getLoadDataList(nil,
|
2021-04-18 00:43:19 +08:00
|
|
|
zap.String("module", wsInstance.GetModuleFlag()),
|
|
|
|
zap.String("command", cmd.GetCommand()),
|
2021-04-18 21:55:41 +08:00
|
|
|
),
|
|
|
|
)
|
2021-04-18 00:43:19 +08:00
|
|
|
commandTable[wsInstance.GetModuleFlag()][cmd.GetCommand()] = cmd
|
|
|
|
}
|
2021-03-28 16:04:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// dispatchCommand 调度command ...
|
|
|
|
//
|
2021-06-05 22:13:09 +08:00
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
2021-03-28 16:04:42 +08:00
|
|
|
//
|
|
|
|
// Date : 3:36 下午 2021/3/28
|
|
|
|
func dispatchCommand(ctx *context.WSContext, data []byte) error {
|
|
|
|
if _, exist := commandTable[ctx.Flag]; !exist {
|
2021-04-18 21:55:41 +08:00
|
|
|
log(
|
|
|
|
getLoggerInstance(ctx.Flag, nil),
|
|
|
|
logFuncFatal,
|
|
|
|
"长连接模块不存在",
|
|
|
|
getLoadDataList(ctx),
|
|
|
|
)
|
2021-03-28 16:04:42 +08:00
|
|
|
return errors.WithStack(errors.New("未注册【" + ctx.Flag + "】长连接模块"))
|
|
|
|
}
|
2021-04-17 15:04:43 +08:00
|
|
|
var (
|
|
|
|
exist bool
|
|
|
|
cmdInstance abstract.ICommand
|
|
|
|
cmdConfig *config.CommandConfig
|
|
|
|
err error
|
2021-04-18 21:55:41 +08:00
|
|
|
result interface{}
|
|
|
|
optionList []config.SetCommandConfig
|
2021-04-17 15:04:43 +08:00
|
|
|
)
|
2021-04-18 21:55:41 +08:00
|
|
|
cmd := gjson.Get(string(data), "command").String()
|
|
|
|
|
2021-04-17 15:04:43 +08:00
|
|
|
if cmdInstance, exist = commandTable[ctx.Flag][cmd]; !exist {
|
2021-04-18 21:55:41 +08:00
|
|
|
log(
|
|
|
|
getLoggerInstance(ctx.Flag, nil),
|
|
|
|
logFuncFatal,
|
|
|
|
"指令未注册",
|
|
|
|
getLoadDataList(ctx),
|
|
|
|
)
|
2021-04-09 16:08:00 +08:00
|
|
|
return errors.WithStack(errors.New("【" + ctx.Flag + "】长连接模块未注册【" + cmd + "】指令"))
|
2021-03-28 16:04:42 +08:00
|
|
|
}
|
2021-04-18 21:55:41 +08:00
|
|
|
|
|
|
|
if optionList = cmdInstance.GetConfigOption(); nil == optionList {
|
2021-04-17 15:04:43 +08:00
|
|
|
optionList = make([]config.SetCommandConfig, 0)
|
|
|
|
}
|
|
|
|
cmdConfig = config.NewCommandConfig(optionList...)
|
2021-04-18 21:55:41 +08:00
|
|
|
log(
|
|
|
|
getLoggerInstance(ctx.Flag, &cmdConfig.LogUpData),
|
|
|
|
logFuncInfo,
|
|
|
|
"上行原始数据记录",
|
|
|
|
getLoadDataList(ctx, zap.String("up_data", string(data))),
|
|
|
|
)
|
|
|
|
|
|
|
|
if result, err = cmdInstance.Execute(ctx, data); nil != err {
|
2021-04-17 15:04:43 +08:00
|
|
|
if cmdConfig.PushMessageWithError {
|
2021-04-18 21:55:41 +08:00
|
|
|
if err := message.Response(ctx, map[string]interface{}{
|
2021-04-17 15:04:43 +08:00
|
|
|
"command": cmd,
|
|
|
|
"message": err.Error(),
|
|
|
|
"success": false,
|
2021-04-18 21:55:41 +08:00
|
|
|
}); nil != err {
|
|
|
|
log(
|
|
|
|
getLoggerInstance(ctx.Flag, nil),
|
|
|
|
logFuncWarn,
|
|
|
|
"指令执行失败",
|
|
|
|
getLoadDataList(ctx, zap.Error(err)),
|
|
|
|
)
|
|
|
|
}
|
2021-04-17 15:04:43 +08:00
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
2021-04-18 21:55:41 +08:00
|
|
|
|
|
|
|
if cmdConfig.ResponseData {
|
|
|
|
responseData := buildResponseData(ctx, cmd, result)
|
|
|
|
if err := ctx.Session.Write(responseData); nil != err {
|
|
|
|
log(
|
|
|
|
getLoggerInstance(ctx.Flag, nil),
|
|
|
|
logFuncWarn,
|
|
|
|
"指令响应结果失败",
|
|
|
|
getLoadDataList(ctx, zap.Error(err), zap.String("expect_response", string(responseData))),
|
|
|
|
)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
log(
|
|
|
|
getLoggerInstance(ctx.Flag, &cmdConfig.LogDownData),
|
|
|
|
logFuncInfo,
|
|
|
|
"指令响应结果记录",
|
|
|
|
getLoadDataList(ctx, zap.String("down_data", string(responseData))),
|
|
|
|
)
|
|
|
|
}
|
2021-04-17 15:04:43 +08:00
|
|
|
return nil
|
2021-03-28 16:04:42 +08:00
|
|
|
}
|
|
|
|
|
2021-04-19 00:03:25 +08:00
|
|
|
// 启动所有端口的监听
|
2021-11-17 18:54:34 +08:00
|
|
|
func run(finishHook func()) {
|
2021-04-19 00:03:25 +08:00
|
|
|
for port, ginInstance := range ginRouterTable {
|
|
|
|
go func(ginInstance *gin.Engine, port int) {
|
|
|
|
if err := ginInstance.Run(fmt.Sprintf(":%d", port)); nil != err {
|
2021-11-17 18:36:57 +08:00
|
|
|
panic(fmt.Sprintf("%d 启动端口监听失败, 失败原因 : %s", port, err.Error()))
|
2021-04-19 00:03:25 +08:00
|
|
|
}
|
|
|
|
}(ginInstance, port)
|
|
|
|
}
|
|
|
|
|
2021-03-28 16:04:42 +08:00
|
|
|
<-sigChan
|
2021-11-17 18:54:34 +08:00
|
|
|
// 增加后置hook
|
|
|
|
if nil != finishHook {
|
|
|
|
finishHook()
|
|
|
|
}
|
2021-03-28 16:04:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Stop 停止服务
|
|
|
|
//
|
2021-06-05 22:13:09 +08:00
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
2021-03-28 16:04:42 +08:00
|
|
|
//
|
|
|
|
// Date : 3:55 下午 2021/3/28
|
|
|
|
func Stop() {
|
|
|
|
sigChan <- 1
|
|
|
|
}
|