From bc656247c9b547c6f0a859ad83722c34815148b6 Mon Sep 17 00:00:00 2001 From: "DESKTOP-V763RJ7\\Administrator" <835606593@qq.com> Date: Sat, 13 Dec 2025 18:22:35 +0800 Subject: [PATCH] =?UTF-8?q?feat=20app=20=E6=A8=A1=E5=9D=97=E5=8C=96?= =?UTF-8?q?=E5=90=AF=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Client/web/game.js | 2 +- Public/Publish/cmd.txt | 6 ++ Public/Publish/docker-compose-db.yml | 31 +++++++ Public/Publish/docker-compose-tool.yml | 26 ++++++ Public/Publish/docker-compose.yml | 81 ------------------- Server/Gateway/Dockerfile | 11 +++ Server/Gateway/app/app.go | 81 +++++++------------ Server/Gateway/app/base.go | 15 +++- Server/Gateway/app/db.go | 19 ++++- Server/Gateway/app/grpc.go | 27 +++++++ Server/Gateway/app/web.go | 39 ++++++++- Server/Gateway/app/websocket.go | 54 ++++++++----- Server/Gateway/config/config.dev.yaml | 10 +-- Server/Gateway/config/config.prod.yaml | 29 +++++++ .../handler/http_handler/helper/base.go | 18 +++++ .../http_handler/helper/render/code.go | 31 +++++++ .../http_handler/helper/render/render.go | 30 +++++++ .../Gateway/handler/http_handler/unique_id.go | 16 ++++ Server/Gateway/net/http_gateway/middleward.go | 37 +++++++++ Server/Gateway/net/http_gateway/router.go | 29 ++++--- Server/build-all.bat | 22 +++++ Server/common/config/loader.go | 8 +- Server/common/db/etcd/etcd.go | 65 +++++++++++++-- Server/common/discover/common/tool.go | 16 ++-- Server/common/discover/instance.go | 5 +- Server/common/discover/listener.go | 11 ++- Server/common/discover/server.go | 5 +- Server/publish/gateway/Dockerfile | 11 +++ .../publish/gateway/config/config.prod.yaml | 29 +++++++ Server/publish/publish.bat | 19 +++++ Server/publish/run_gateway.sh | 7 ++ Server/publish/run_scene.sh | 7 ++ Server/publish/scene/Dockerfile | 11 +++ Server/publish/scene/config/config.prod.yaml | 19 +++++ Server/scene/Dockerfile | 11 +++ Server/scene/app/app.go | 61 +++++++------- Server/scene/app/base.go | 15 +++- Server/scene/app/db.go | 19 ++++- Server/scene/app/grpc.go | 27 +++++++ Server/scene/config/config.dev.yaml | 4 +- Server/scene/config/config.prod.yaml | 19 +++++ 41 files changed, 730 insertions(+), 253 deletions(-) create mode 100644 Public/Publish/cmd.txt create mode 100644 Public/Publish/docker-compose-db.yml create mode 100644 Public/Publish/docker-compose-tool.yml delete mode 100644 Public/Publish/docker-compose.yml create mode 100644 Server/Gateway/Dockerfile create mode 100644 Server/Gateway/app/grpc.go create mode 100644 Server/Gateway/config/config.prod.yaml create mode 100644 Server/Gateway/handler/http_handler/helper/base.go create mode 100644 Server/Gateway/handler/http_handler/helper/render/code.go create mode 100644 Server/Gateway/handler/http_handler/helper/render/render.go create mode 100644 Server/Gateway/handler/http_handler/unique_id.go create mode 100644 Server/Gateway/net/http_gateway/middleward.go create mode 100644 Server/build-all.bat create mode 100644 Server/publish/gateway/Dockerfile create mode 100644 Server/publish/gateway/config/config.prod.yaml create mode 100644 Server/publish/publish.bat create mode 100644 Server/publish/run_gateway.sh create mode 100644 Server/publish/run_scene.sh create mode 100644 Server/publish/scene/Dockerfile create mode 100644 Server/publish/scene/config/config.prod.yaml create mode 100644 Server/scene/Dockerfile create mode 100644 Server/scene/app/grpc.go create mode 100644 Server/scene/config/config.prod.yaml diff --git a/Client/web/game.js b/Client/web/game.js index 79255c9..3c1fb95 100644 --- a/Client/web/game.js +++ b/Client/web/game.js @@ -10,7 +10,7 @@ class WebSocketService { } connect(squareId, onMessage, onStatusChange) { - const wsUrl = `ws://localhost:8501/?token=${squareId}`; + const wsUrl = `wss://www.hlsq.asia/ws/?token=${squareId}`; try { const ws = new WebSocket(wsUrl); diff --git a/Public/Publish/cmd.txt b/Public/Publish/cmd.txt new file mode 100644 index 0000000..feeb592 --- /dev/null +++ b/Public/Publish/cmd.txt @@ -0,0 +1,6 @@ +docker compose -p db -f docker-compose-db.yml up -d +docker compose -p tool -f docker-compose-tool.yml up -d + +chown -R 1000:1000 jenkins/ + +ssh -L 2379:localhost:2379 root@47.108.184.184 \ No newline at end of file diff --git a/Public/Publish/docker-compose-db.yml b/Public/Publish/docker-compose-db.yml new file mode 100644 index 0000000..23de485 --- /dev/null +++ b/Public/Publish/docker-compose-db.yml @@ -0,0 +1,31 @@ +services: + mysql: + image: mysql:latest + container_name: mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: gR9pV4tY7zR6qL3e + TZ: Asia/Shanghai + ports: + - "3306:3306" + volumes: + - ./db/mysql:/var/lib/mysql + + redis: + image: redis:latest + container_name: redis + restart: always + ports: + - "6379:6379" + volumes: + - ./db/redis:/data + command: redis-server --requirepass lQ7aM8oB6lK0iD5k + + etcd: + image: bitnami/etcd:latest + container_name: etcd + restart: always + ports: + - "2379:2379" + environment: + ALLOW_NONE_AUTHENTICATION: "yes" diff --git a/Public/Publish/docker-compose-tool.yml b/Public/Publish/docker-compose-tool.yml new file mode 100644 index 0000000..146b366 --- /dev/null +++ b/Public/Publish/docker-compose-tool.yml @@ -0,0 +1,26 @@ +services: + jenkins: + image: jenkins/jenkins:lts + container_name: jenkins + command: "--prefix=/jenkins" + restart: unless-stopped + ports: + - "8080:8080" + volumes: + - ./jenkins:/var/jenkins_home + - /var/run/docker.sock:/var/run/docker.sock + - /usr/bin/docker:/usr/bin/docker + environment: + - TZ=Asia/Shanghai + + nginx: + image: nginx:alpine + container_name: nginx + restart: unless-stopped + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/conf.d:/etc/nginx/conf.d:ro + - ./nginx/html:/var/www/html + - ./nginx/certs:/etc/nginx/certs:ro diff --git a/Public/Publish/docker-compose.yml b/Public/Publish/docker-compose.yml deleted file mode 100644 index 815d483..0000000 --- a/Public/Publish/docker-compose.yml +++ /dev/null @@ -1,81 +0,0 @@ -version: '3.8' -services: - # ETCD 分布式键值存储 - etcd: - image: quay.io/coreos/etcd:v3.5.4 - container_name: etcd - ports: - - "2379:2379" # 客户端连接端口 - - "2380:2380" # 节点通信端口 - environment: - ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379" - ETCD_ADVERTISE_CLIENT_URLS: "http://etcd:2379" - ETCD_LISTEN_PEER_URLS: "http://0.0.0.0:2380" - ETCD_INITIAL_ADVERTISE_PEER_URLS: "http://etcd:2380" - ETCD_NAME: "etcd-single" - ETCD_INITIAL_CLUSTER: "etcd-single=http://etcd:2380" - ETCD_INITIAL_CLUSTER_TOKEN: "etcd-cluster" - ETCD_INITIAL_CLUSTER_STATE: "new" - volumes: - - etcd-data:/etcd-data - networks: - - app-network - - # Redis 缓存数据库 - redis: - image: redis:7.0-alpine - container_name: redis - ports: - - "6379:6379" - volumes: - - redis-data:/data - networks: - - app-network - command: redis-server --save 60 1 --loglevel warning - - # MySQL 数据库 - mysql: - image: mysql:8.0 - container_name: mysql - environment: - MYSQL_ROOT_PASSWORD: rootpassword # 请更改为强密码 - MYSQL_DATABASE: app_db - MYSQL_USER: app_user - MYSQL_PASSWORD: userpassword # 请更改为强密码 - ports: - - "3306:3306" - volumes: - - mysql-data:/var/lib/mysql - - ./mysql-init:/docker-entrypoint-initdb.d # 初始化SQL脚本目录 - networks: - - app-network - healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] - interval: 5s - timeout: 10s - retries: 5 - - # ETCD 可视化界面 (Web UI) - etcd-viewer: - image: deltaprojects/etcdkeeper:latest - container_name: etcd-viewer - ports: - - "8080:8080" - environment: - ETCD_HOST: etcd - ETCD_PORT: 2379 - networks: - - app-network - depends_on: - - etcd - -# 数据卷声明 -volumes: - etcd-data: - redis-data: - mysql-data: - -# 网络配置 -networks: - app-network: - driver: bridge \ No newline at end of file diff --git a/Server/Gateway/Dockerfile b/Server/Gateway/Dockerfile new file mode 100644 index 0000000..b5dc514 --- /dev/null +++ b/Server/Gateway/Dockerfile @@ -0,0 +1,11 @@ +FROM alpine:latest + +RUN apk add --no-cache tzdata && \ + ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ + echo "Asia/Shanghai" > /etc/timezone + +COPY gateway/ /app/ +RUN chmod 777 /app/server-gateway + +WORKDIR /app +CMD ["./server-gateway"] \ No newline at end of file diff --git a/Server/Gateway/app/app.go b/Server/Gateway/app/app.go index ff0ed33..a3df205 100644 --- a/Server/Gateway/app/app.go +++ b/Server/Gateway/app/app.go @@ -1,81 +1,60 @@ package app import ( - "common/db/etcd" "common/discover" "common/log" - "common/net/grpc/service" - "common/net/socket/websocket" "fmt" "gateway/config" - "gateway/grpc_server/server" - "github.com/gin-gonic/gin" "github.com/judwhite/go-svc" - "runtime/debug" - "sync" ) type Program struct { - wg *sync.WaitGroup - server service.IService // grpc服务 - webServer *gin.Engine // web服务 - wsServer *websocket.WSServer // websocket服务 - stop chan bool + moduleList []Module // 模块列表 +} + +type Module interface { + Init() error + Start() error + Stop() error } func (p *Program) Init(_ svc.Environment) error { - if err := p.initBase(); err != nil { - return err - } - cfg := config.Get() - log.Infof(fmt.Sprintf("%v starting...", cfg.App.Name)) - if err := p.initDB(cfg); err != nil { - return err - } - if err := p.initWebServer(cfg); err != nil { - return err - } - if err := p.initWsServer(cfg); err != nil { - return err + p.moduleList = append(p.moduleList, &ModuleBase{}) + p.moduleList = append(p.moduleList, &ModuleDB{}) + p.moduleList = append(p.moduleList, &ModuleWebServer{}) + p.moduleList = append(p.moduleList, &ModuleWebsocketServer{}) + p.moduleList = append(p.moduleList, &ModuleGrpcServer{}) + + for _, module := range p.moduleList { + if err := module.Init(); err != nil { + return err + } } + log.Infof(fmt.Sprintf("%v Init successful...", config.Get().App.Name)) return nil } func (p *Program) Start() error { - defer func() { - if err := recover(); err != nil { - fmt.Printf("Start err: %v", err) - debug.PrintStack() - _ = p.Stop() + for _, module := range p.moduleList { + if err := module.Start(); err != nil { + return err } - }() - + } discover.Listen() - p.server = server.NewServer(config.Get().Serve.Grpc.TTL) - p.server.Init(config.Get().Serve.Grpc.Address, config.Get().Serve.Grpc.Port) - go func() { - cfg := config.Get() - _ = p.wsServer.Run( - log.GetLogger().Named("gnet"), - fmt.Sprintf("tcp4://0.0.0.0:%v", cfg.Serve.Socket.Web.Port), - true, true, false, false, true, 8, - ) - }() + log.Infof(fmt.Sprintf("%v Start successful...", config.Get().App.Name)) return nil } func (p *Program) Stop() error { - defer func() { - if err := recover(); err != nil { - fmt.Printf("Stop err: %v", err) - debug.PrintStack() - } - }() - discover.Close() - p.server.Close() - _ = etcd.Close() + for i := len(p.moduleList) - 1; i >= 0; i-- { + module := p.moduleList[i] + if err := module.Stop(); err != nil { + log.Errorf("module stop error: %v", err) + } + } + log.Infof(fmt.Sprintf("%v Stop successful...", config.Get().App.Name)) return nil } diff --git a/Server/Gateway/app/base.go b/Server/Gateway/app/base.go index 015a756..6a87808 100644 --- a/Server/Gateway/app/base.go +++ b/Server/Gateway/app/base.go @@ -7,7 +7,11 @@ import ( "math/rand" ) -func (p *Program) initBase() error { +// ModuleBase 基础模块,或者一些零散的模块 +type ModuleBase struct { +} + +func (p *ModuleBase) Init() error { // 配置 if err := config.LoadConfig(); err != nil { return err @@ -17,6 +21,13 @@ func (p *Program) initBase() error { log.Init(cfg.Log.Debug, cfg.Log.MaxSize, cfg.Log.MaxBackups, cfg.Log.MaxAge, cfg.Log.Level) // 雪花 utils.InitSnowflake(int64(rand.Intn(1000))) - + return nil +} + +func (p *ModuleBase) Start() error { + return nil +} + +func (p *ModuleBase) Stop() error { return nil } diff --git a/Server/Gateway/app/db.go b/Server/Gateway/app/db.go index 5011668..aaa941e 100644 --- a/Server/Gateway/app/db.go +++ b/Server/Gateway/app/db.go @@ -2,10 +2,16 @@ package app import ( "common/db/etcd" + "common/log" "gateway/config" ) -func (p *Program) initDB(cfg *config.Config) error { +// ModuleDB 数据库模块 +type ModuleDB struct { +} + +func (p *ModuleDB) Init() error { + cfg := config.Get() // ETCD if err := etcd.Init(cfg.DB.Etcd.Address); err != nil { return err @@ -13,8 +19,13 @@ func (p *Program) initDB(cfg *config.Config) error { return nil } -func (p *Program) stopDB() error { - _ = etcd.Close() - +func (p *ModuleDB) Start() error { + return nil +} + +func (p *ModuleDB) Stop() error { + if err := etcd.Close(); err != nil { + log.Errorf("close etcd failed: %v", err) + } return nil } diff --git a/Server/Gateway/app/grpc.go b/Server/Gateway/app/grpc.go new file mode 100644 index 0000000..431376d --- /dev/null +++ b/Server/Gateway/app/grpc.go @@ -0,0 +1,27 @@ +package app + +import ( + "common/net/grpc/service" + "gateway/config" + "gateway/grpc_server/server" +) + +// ModuleGrpcServer Grpc服务模块 +type ModuleGrpcServer struct { + server service.IService +} + +func (m *ModuleGrpcServer) Init() error { + return nil +} + +func (m *ModuleGrpcServer) Start() error { + m.server = server.NewServer(config.Get().Serve.Grpc.TTL) + m.server.Init(config.Get().Serve.Grpc.Address, config.Get().Serve.Grpc.Port) + return nil +} + +func (m *ModuleGrpcServer) Stop() error { + m.server.Close() + return nil +} diff --git a/Server/Gateway/app/web.go b/Server/Gateway/app/web.go index 077f589..85ddbbc 100644 --- a/Server/Gateway/app/web.go +++ b/Server/Gateway/app/web.go @@ -1,16 +1,49 @@ package app import ( + "common/log" + "errors" + "fmt" "gateway/config" "gateway/net/http_gateway" + "github.com/gin-gonic/gin" + "net/http" + "sync" ) -func (p *Program) initWebServer(cfg *config.Config) error { - p.webServer = http_gateway.InitRouter(cfg) +// ModuleWebServer Web服务模块 +type ModuleWebServer struct { + wg *sync.WaitGroup + server *http.Server + router *gin.Engine +} +func (m *ModuleWebServer) Init() error { + m.wg = &sync.WaitGroup{} + m.router = http_gateway.InitRouter(config.Get()) return nil } -func (p *Program) stopWebServer() error { +func (m *ModuleWebServer) Start() error { + m.wg.Add(1) + go func() { + defer m.wg.Done() + m.server = &http.Server{ + Addr: fmt.Sprintf("%v:%v", config.Get().Serve.Http.Address, config.Get().Serve.Http.Port), + Handler: m.router, + } + if err := m.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + log.Errorf("http server failed: %v", err.Error()) + } + log.Infof("http server stop.") + }() + return nil +} + +func (m *ModuleWebServer) Stop() error { + if err := m.server.Close(); err != nil { + log.Errorf("stop http server failed: %v", err) + } + m.wg.Wait() return nil } diff --git a/Server/Gateway/app/websocket.go b/Server/Gateway/app/websocket.go index c4b8988..75a83bc 100644 --- a/Server/Gateway/app/websocket.go +++ b/Server/Gateway/app/websocket.go @@ -3,37 +3,51 @@ package app import ( "common/log" "common/net/socket/websocket" + "fmt" "gateway/config" "gateway/net/ws_gateway" + "sync" "time" ) -func (p *Program) initWsServer(cfg *config.Config) error { - //p.wsServer = websocket.NewWSServer( - // &ws_gateway.GatewayWsServer{ - // Decoder: &ws_gateway.Decoder{}, - // }, - // log.GetLogger(), - // fmt.Sprintf("tcp4://%v:%d", cfg.SocketServer.Host, cfg.SocketServer.Port), - // true, - // true, - // false, - // false, - // true, - // 8, - // 5*time.Second, - //) - p.wsServer = websocket.NewWSServer( +// ModuleWebsocketServer Websocket服务模块 +type ModuleWebsocketServer struct { + wg *sync.WaitGroup + server *websocket.WSServer +} + +func (m *ModuleWebsocketServer) Init() error { + m.wg = &sync.WaitGroup{} + m.server = websocket.NewWSServer( &ws_gateway.GatewayWsServer{}, log.GetLogger().Named("ws_server"), 5*time.Second, ) - return nil } -func (p *Program) stopWsServer() error { - _ = p.wsServer.Stop() - +func (m *ModuleWebsocketServer) Start() error { + m.wg.Add(1) + go func() { + defer m.wg.Done() + _ = m.server.Run( + log.GetLogger().Named("GNET"), + fmt.Sprintf("tcp4://0.0.0.0:%v", config.Get().Serve.Socket.Web.Port), + true, + true, + false, + false, + true, + 8, + ) + }() + return nil +} + +func (m *ModuleWebsocketServer) Stop() error { + if err := m.server.Stop(); err != nil { + log.Errorf("stop websocket server failed: %v", err) + } + m.wg.Wait() return nil } diff --git a/Server/Gateway/config/config.dev.yaml b/Server/Gateway/config/config.dev.yaml index c5f7c04..7ae5003 100644 --- a/Server/Gateway/config/config.dev.yaml +++ b/Server/Gateway/config/config.dev.yaml @@ -4,8 +4,8 @@ app: log: debug: true level: "debug" - maxSize: 100 - maxBackups: 3 + maxSize: 10 + maxBackups: 100 maxAge: 7 db: @@ -19,11 +19,11 @@ serve: ttl: 20 socket: web: - address: "0.0.0.0" + address: "127.0.0.1" port: 8501 raw: - address: "0.0.0.0" + address: "127.0.0.1" port: 8502 http: - address: "0.0.0.0" + address: "127.0.0.1" port: 8503 diff --git a/Server/Gateway/config/config.prod.yaml b/Server/Gateway/config/config.prod.yaml new file mode 100644 index 0000000..586a8dd --- /dev/null +++ b/Server/Gateway/config/config.prod.yaml @@ -0,0 +1,29 @@ +app: + name: "gateway-prod" + +log: + debug: false + level: "debug" + maxSize: 10 + maxBackups: 100 + maxAge: 7 + +db: + etcd: + address: [ "172.18.28.0:2379" ] + +serve: + grpc: + address: "172.18.28.0" + port: 8500 + ttl: 20 + socket: + web: + address: "172.18.28.0" + port: 8501 + raw: + address: "172.18.28.0" + port: 8502 + http: + address: "172.18.28.0" + port: 8503 diff --git a/Server/Gateway/handler/http_handler/helper/base.go b/Server/Gateway/handler/http_handler/helper/base.go new file mode 100644 index 0000000..5b02db8 --- /dev/null +++ b/Server/Gateway/handler/http_handler/helper/base.go @@ -0,0 +1,18 @@ +package helper + +type Pagination struct { + Page int `json:"page"` // 页 + Size int `json:"size"` // 页大小 +} + +//func SetBaseQuery(page *Pagination, order string) func(db *gorm.DB) *gorm.DB { +// return func(db *gorm.DB) *gorm.DB { +// if page != nil { +// db = db.Offset((page.Page - 1) * page.Size).Limit(page.Size) +// } +// if len(order) > 0 { +// db = db.Order(order) +// } +// return db +// } +//} diff --git a/Server/Gateway/handler/http_handler/helper/render/code.go b/Server/Gateway/handler/http_handler/helper/render/code.go new file mode 100644 index 0000000..db47dc5 --- /dev/null +++ b/Server/Gateway/handler/http_handler/helper/render/code.go @@ -0,0 +1,31 @@ +package render + +var ( + OK = NewCode(0, "成功") + Failed = NewCode(1, "失败") + ParamError = NewCode(1001, "参数错误") + NameEmpty = NewCode(1002, "名称不能为空") + NameDuplicate = NewCode(1003, "名称或编号不能重复") + ListEmpty = NewCode(1004, "列表不能为空") + RepeatCommit = NewCode(1005, "请勿重复提交") +) + +type Code struct { + code int + message string +} + +func NewCode(code int, message string) *Code { + return &Code{ + code: code, + message: message, + } +} + +func (c *Code) Code() int { + return c.code +} + +func (c *Code) Message() string { + return c.message +} diff --git a/Server/Gateway/handler/http_handler/helper/render/render.go b/Server/Gateway/handler/http_handler/helper/render/render.go new file mode 100644 index 0000000..a928ba4 --- /dev/null +++ b/Server/Gateway/handler/http_handler/helper/render/render.go @@ -0,0 +1,30 @@ +package render + +import ( + "github.com/gin-gonic/gin" + "net/http" +) + +type RespJsonData struct { + Code int `json:"code"` + Msg string `json:"msg"` + Data interface{} `json:"data"` +} + +func Json(c *gin.Context, code *Code, data interface{}) { + result := &RespJsonData{ + Code: code.Code(), + Msg: code.Message(), + Data: data, + } + c.JSON(http.StatusOK, result) +} + +func AbortJson(c *gin.Context, code *Code, data interface{}) { + result := &RespJsonData{ + Code: code.Code(), + Msg: code.Message(), + Data: data, + } + c.AbortWithStatusJSON(http.StatusOK, result) +} diff --git a/Server/Gateway/handler/http_handler/unique_id.go b/Server/Gateway/handler/http_handler/unique_id.go new file mode 100644 index 0000000..175b0e5 --- /dev/null +++ b/Server/Gateway/handler/http_handler/unique_id.go @@ -0,0 +1,16 @@ +package http_handler + +import ( + "common/utils" + "gateway/handler/http_handler/helper/render" + "github.com/gin-gonic/gin" +) + +type UniqueIDResp struct { + ID string `json:"id"` +} + +// GenSnowflake 生成雪花ID +func GenSnowflake(c *gin.Context) { + render.Json(c, render.OK, &UniqueIDResp{ID: utils.SnowflakeInstance().Generate().String()}) +} diff --git a/Server/Gateway/net/http_gateway/middleward.go b/Server/Gateway/net/http_gateway/middleward.go new file mode 100644 index 0000000..72a381c --- /dev/null +++ b/Server/Gateway/net/http_gateway/middleward.go @@ -0,0 +1,37 @@ +package http_gateway + +import ( + "fmt" + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" + "go.uber.org/zap" + "time" +) + +func corsConfig() cors.Config { + return cors.Config{ + AllowMethods: []string{"GET", "POST", "OPTIONS"}, + AllowHeaders: []string{"Content-Type", "Authorization"}, + AllowCredentials: false, + AllowAllOrigins: true, + MaxAge: 12 * time.Hour, + } +} + +func ginLogger(logger *zap.SugaredLogger) gin.HandlerFunc { + return func(c *gin.Context) { + start := time.Now() + path := c.Request.URL.Path + c.Next() + cost := time.Since(start) + + logger.Infof(fmt.Sprintf( + "HTTP Method:%v Code:%v Time:%v IP:%v Path:%v", + c.Request.Method, + c.Writer.Status(), + cost, + c.ClientIP(), + path), + ) + } +} diff --git a/Server/Gateway/net/http_gateway/router.go b/Server/Gateway/net/http_gateway/router.go index d39ca46..7757b2c 100644 --- a/Server/Gateway/net/http_gateway/router.go +++ b/Server/Gateway/net/http_gateway/router.go @@ -1,44 +1,43 @@ package http_gateway import ( + "common/log" "gateway/config" + "gateway/handler/http_handler" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "net/http" - "time" ) func InitRouter(cfg *config.Config) *gin.Engine { - r := gin.Default() - r.Use(gin.Recovery()) - r.Use(cors.New(getCorsConfig())) + gin.SetMode(gin.ReleaseMode) + + r := gin.New() + r.Use( + gin.Recovery(), + ginLogger(log.GetLogger().Named("GIN")), + cors.New(corsConfig()), + ) - r.MaxMultipartMemory = 8 << 20 r.HandleMethodNotAllowed = true r.NoMethod(func(c *gin.Context) { c.JSON(http.StatusMethodNotAllowed, gin.H{ "result": false, "error": "Method Not Allowed", }) - return }) r.NoRoute(func(c *gin.Context) { c.JSON(http.StatusNotFound, gin.H{ "result": false, "error": "Endpoint Not Found", }) - return }) + initBaseRouter(r) return r } -func getCorsConfig() cors.Config { - return cors.Config{ - AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"}, - AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization", "X-Forwarded-For", "User-Agent", "Referer", "X-Token", "token", "Token", "company-id", "lang", "source"}, - AllowCredentials: false, - AllowAllOrigins: true, - MaxAge: 12 * time.Hour, - } +func initBaseRouter(router *gin.Engine) { + g := router.Group("/b") + g.POST("/snowflake", http_handler.GenSnowflake) // 生成雪花 } diff --git a/Server/build-all.bat b/Server/build-all.bat new file mode 100644 index 0000000..a53c313 --- /dev/null +++ b/Server/build-all.bat @@ -0,0 +1,22 @@ +@echo off +setlocal +cd "%~dp0" + +set GOOS=linux +set GOARCH=amd64 + +echo [INFO] Build started... +echo. + +for %%p in (gateway scene) do ( + echo [BUILD] server-%%p... + cd %%p + go build -o server-%%p + move server-%%p .. >nul + cd .. +) + +echo. +echo [INFO] Build finished successfully! + +endlocal \ No newline at end of file diff --git a/Server/common/config/loader.go b/Server/common/config/loader.go index f10be0c..143e282 100644 --- a/Server/common/config/loader.go +++ b/Server/common/config/loader.go @@ -1,6 +1,7 @@ package config import ( + "encoding/json" "fmt" "github.com/spf13/viper" "strings" @@ -28,11 +29,14 @@ func LoadConfig[T any](configDir string, configPtr *T) (*T, error) { v.SetConfigType("yaml") if err := v.ReadInConfig(); err != nil { - return nil, fmt.Errorf("读取配置失败: %w", err) + return nil, fmt.Errorf("failed to read config: %w", err) } if err := v.Unmarshal(&configPtr); err != nil { - return nil, fmt.Errorf("解析配置失败: %w", err) + return nil, fmt.Errorf("failed to unmarshal config: %w", err) } + + marshal, _ := json.Marshal(configPtr) + fmt.Printf("Configuration loading completed: %v\n", string(marshal)) return configPtr, nil } diff --git a/Server/common/db/etcd/etcd.go b/Server/common/db/etcd/etcd.go index 9a828f4..92212b4 100644 --- a/Server/common/db/etcd/etcd.go +++ b/Server/common/db/etcd/etcd.go @@ -1,28 +1,77 @@ package etcd import ( + "context" "go.etcd.io/etcd/client/v3" + "go.uber.org/zap" "time" ) -var cli *clientv3.Client +var instance *Client +type Client struct { + cli *clientv3.Client + log *zap.Logger +} + +// Init 初始化 func Init(endpoints []string) error { client, err := clientv3.New(clientv3.Config{ Endpoints: endpoints, DialTimeout: 5 * time.Second, }) - cli = client + instance = &Client{ + cli: client, + } return err } -func Client() *clientv3.Client { - return cli -} - +// Close 关闭 func Close() error { - if cli != nil { - return cli.Close() + if instance != nil && instance.cli != nil { + return instance.cli.Close() } return nil } + +func GetClient() *Client { + return instance +} + +// Get 获取数据 +func (c *Client) Get(key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + return c.cli.Get(ctx, key, opts...) +} + +// Put 创建数据 +func (c *Client) Put(key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + return c.cli.Put(ctx, key, val, opts...) +} + +// Grant 创建租约 +func (c *Client) Grant(ttl int64) (*clientv3.LeaseGrantResponse, error) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + return c.cli.Grant(ctx, ttl) +} + +// KeepAlive 保活租约 +func (c *Client) KeepAlive(id clientv3.LeaseID) (<-chan *clientv3.LeaseKeepAliveResponse, error) { + return c.cli.KeepAlive(context.Background(), id) +} + +// Revoke 撤销租约 +func (c *Client) Revoke(id clientv3.LeaseID) (*clientv3.LeaseRevokeResponse, error) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + return c.cli.Revoke(ctx, id) +} + +// Watch 监听数据 +func (c *Client) Watch(key string, opts ...clientv3.OpOption) clientv3.WatchChan { + return c.cli.Watch(context.Background(), key, opts...) +} diff --git a/Server/common/discover/common/tool.go b/Server/common/discover/common/tool.go index a315603..3a1bbc1 100644 --- a/Server/common/discover/common/tool.go +++ b/Server/common/discover/common/tool.go @@ -3,27 +3,23 @@ package common import ( "common/db/etcd" "common/log" - "context" clientv3 "go.etcd.io/etcd/client/v3" ) // NewLeaseAndKeepAlive 创建租约并保活 func NewLeaseAndKeepAlive(ttl int64) (clientv3.LeaseID, error) { - lease, err := etcd.Client().Grant(context.Background(), ttl) + lease, err := etcd.GetClient().Grant(ttl) if err != nil { return 0, err } - chKeepAlive, err := etcd.Client().KeepAlive(context.Background(), lease.ID) + chKeepAlive, err := etcd.GetClient().KeepAlive(lease.ID) if err != nil { return 0, err } - go func() { - for r := range chKeepAlive { - if r == nil { - log.Errorf("lease timeout!") - return - } + go func(leaseID clientv3.LeaseID) { + for range chKeepAlive { } - }() + log.Warnf("Lease %x expired or revoked", leaseID) + }(lease.ID) return lease.ID, nil } diff --git a/Server/common/discover/instance.go b/Server/common/discover/instance.go index 8586a6f..8f10241 100644 --- a/Server/common/discover/instance.go +++ b/Server/common/discover/instance.go @@ -4,7 +4,6 @@ import ( "common/db/etcd" "common/discover/common" "common/log" - "context" "fmt" clientv3 "go.etcd.io/etcd/client/v3" "strconv" @@ -42,7 +41,7 @@ func RegisterInstance(sid int64, instanceID int, uniqueNo, ttl int64) error { return err } key := fmt.Sprintf("%v/%v/%v", common.KeyDiscoverInstance, instanceID, uniqueNo) - _, err = etcd.Client().Put(context.Background(), key, strconv.Itoa(int(sid)), clientv3.WithLease(leaseID)) + _, err = etcd.GetClient().Put(key, strconv.Itoa(int(sid)), clientv3.WithLease(leaseID)) if err != nil { return err } @@ -55,7 +54,7 @@ func UnRegisterInstance(uniqueNo int64) { serverMU.Lock() defer serverMU.Unlock() if leaseID, ok := instanceLeaseM[uniqueNo]; ok { - _, err := etcd.Client().Revoke(context.Background(), leaseID) + _, err := etcd.GetClient().Revoke(leaseID) if err != nil { log.Errorf("UnRegisterInstance err: %v", err) } diff --git a/Server/common/discover/listener.go b/Server/common/discover/listener.go index 51152f2..38fde40 100644 --- a/Server/common/discover/listener.go +++ b/Server/common/discover/listener.go @@ -3,8 +3,10 @@ package discover import ( "common/db/etcd" "common/discover/common" + "common/log" "common/utils" "context" + "fmt" "go.etcd.io/etcd/api/v3/mvccpb" clientv3 "go.etcd.io/etcd/client/v3" "strconv" @@ -46,18 +48,19 @@ func Listen() { wg.Add(1) go func() { defer wg.Done() + log.Infof(fmt.Sprintf("Discover start listen...")) // 服务 - serviceAll, _ := etcd.Client().Get(stopCtx, common.KeyDiscoverService, clientv3.WithPrefix()) + serviceAll, _ := etcd.GetClient().Get(common.KeyDiscoverService, clientv3.WithPrefix()) for _, kv := range serviceAll.Kvs { onServerChange(clientv3.EventTypePut, string(kv.Key), string(kv.Value)) } - chService := etcd.Client().Watch(stopCtx, common.KeyDiscoverService, clientv3.WithPrefix(), clientv3.WithRev(serviceAll.Header.Revision+1)) + chService := etcd.GetClient().Watch(common.KeyDiscoverService, clientv3.WithPrefix(), clientv3.WithRev(serviceAll.Header.Revision+1)) // 副本 - instanceAll, _ := etcd.Client().Get(stopCtx, common.KeyDiscoverInstance, clientv3.WithPrefix()) + instanceAll, _ := etcd.GetClient().Get(common.KeyDiscoverInstance, clientv3.WithPrefix()) for _, kv := range instanceAll.Kvs { onInstanceChange(clientv3.EventTypePut, string(kv.Key), string(kv.Value), nil) } - chInstance := etcd.Client().Watch(stopCtx, common.KeyDiscoverScene, clientv3.WithPrefix(), clientv3.WithRev(instanceAll.Header.Revision+1), clientv3.WithPrevKV()) + chInstance := etcd.GetClient().Watch(common.KeyDiscoverScene, clientv3.WithPrefix(), clientv3.WithRev(instanceAll.Header.Revision+1), clientv3.WithPrevKV()) for { select { case msg := <-chService: diff --git a/Server/common/discover/server.go b/Server/common/discover/server.go index 778c38e..903a370 100644 --- a/Server/common/discover/server.go +++ b/Server/common/discover/server.go @@ -5,7 +5,6 @@ import ( "common/discover/common" "common/log" "common/net/grpc/grpc_conn" - "context" "fmt" clientv3 "go.etcd.io/etcd/client/v3" "google.golang.org/grpc" @@ -51,7 +50,7 @@ func RegisterGrpcServer(target string, sid int64, addr string, ttl int64) error if err != nil { return err } - _, err = etcd.Client().Put(context.Background(), fmt.Sprintf("%v/%v", target, sid), addr, clientv3.WithLease(leaseID)) + _, err = etcd.GetClient().Put(fmt.Sprintf("%v/%v", target, sid), addr, clientv3.WithLease(leaseID)) if err != nil { return err } @@ -64,7 +63,7 @@ func UnRegisterGrpcServer(sid int64) { serverMU.Lock() defer serverMU.Unlock() if leaseID, ok := serverLeaseM[sid]; ok { - _, err := etcd.Client().Revoke(context.Background(), leaseID) + _, err := etcd.GetClient().Revoke(leaseID) if err != nil { log.Errorf("server.go UnRegisterGrpcServer err: %v", err) } diff --git a/Server/publish/gateway/Dockerfile b/Server/publish/gateway/Dockerfile new file mode 100644 index 0000000..b5dc514 --- /dev/null +++ b/Server/publish/gateway/Dockerfile @@ -0,0 +1,11 @@ +FROM alpine:latest + +RUN apk add --no-cache tzdata && \ + ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ + echo "Asia/Shanghai" > /etc/timezone + +COPY gateway/ /app/ +RUN chmod 777 /app/server-gateway + +WORKDIR /app +CMD ["./server-gateway"] \ No newline at end of file diff --git a/Server/publish/gateway/config/config.prod.yaml b/Server/publish/gateway/config/config.prod.yaml new file mode 100644 index 0000000..586a8dd --- /dev/null +++ b/Server/publish/gateway/config/config.prod.yaml @@ -0,0 +1,29 @@ +app: + name: "gateway-prod" + +log: + debug: false + level: "debug" + maxSize: 10 + maxBackups: 100 + maxAge: 7 + +db: + etcd: + address: [ "172.18.28.0:2379" ] + +serve: + grpc: + address: "172.18.28.0" + port: 8500 + ttl: 20 + socket: + web: + address: "172.18.28.0" + port: 8501 + raw: + address: "172.18.28.0" + port: 8502 + http: + address: "172.18.28.0" + port: 8503 diff --git a/Server/publish/publish.bat b/Server/publish/publish.bat new file mode 100644 index 0000000..4cfa6e4 --- /dev/null +++ b/Server/publish/publish.bat @@ -0,0 +1,19 @@ +@echo off + +call ../build-all.bat +echo. + +rd /s /q gateway +xcopy ..\gateway\config\config.prod.yaml gateway\config\ >nul +xcopy ..\gateway\Dockerfile gateway\ >nul +move ..\server-gateway gateway\ >nul +echo [INFO] Copy gateway finished. + +rd /s /q scene +xcopy ..\scene\config\config.prod.yaml scene\config\ >nul +xcopy ..\scene\Dockerfile scene\ >nul +move ..\server-scene scene\ >nul +echo [INFO] Copy scene finished. + +echo. +pause \ No newline at end of file diff --git a/Server/publish/run_gateway.sh b/Server/publish/run_gateway.sh new file mode 100644 index 0000000..3fd401b --- /dev/null +++ b/Server/publish/run_gateway.sh @@ -0,0 +1,7 @@ +#!/bin/bash +docker stop server-gateway +docker rm server-gateway +docker rmi server-gateway:latest + +docker build -f ./gateway/Dockerfile . -t server-gateway +docker run -d --name server-gateway -p 8500-8503:8500-8503 --privileged=true --env XH_G_ENV=prod -v /root/server/logs/gateway_log/:/app/logs server-gateway \ No newline at end of file diff --git a/Server/publish/run_scene.sh b/Server/publish/run_scene.sh new file mode 100644 index 0000000..4b0cb7b --- /dev/null +++ b/Server/publish/run_scene.sh @@ -0,0 +1,7 @@ +#!/bin/bash +docker stop server-scene +docker rm server-scene +docker rmi server-scene:latest + +docker build -f ./scene/Dockerfile . -t server-scene +docker run -d --name server-scene -p 8504:8504 --privileged=true --env XH_G_ENV=prod -v /root/server/logs/scene_log/:/app/logs server-scene \ No newline at end of file diff --git a/Server/publish/scene/Dockerfile b/Server/publish/scene/Dockerfile new file mode 100644 index 0000000..2212be1 --- /dev/null +++ b/Server/publish/scene/Dockerfile @@ -0,0 +1,11 @@ +FROM alpine:latest + +RUN apk add --no-cache tzdata && \ + ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ + echo "Asia/Shanghai" > /etc/timezone + +COPY scene/ /app/ +RUN chmod 777 /app/server-scene + +WORKDIR /app +CMD ["./server-scene"] \ No newline at end of file diff --git a/Server/publish/scene/config/config.prod.yaml b/Server/publish/scene/config/config.prod.yaml new file mode 100644 index 0000000..9b0d7a5 --- /dev/null +++ b/Server/publish/scene/config/config.prod.yaml @@ -0,0 +1,19 @@ +app: + name: "scene-prod" + +log: + debug: false + level: "debug" + maxSize: 10 + maxBackups: 100 + maxAge: 7 + +db: + etcd: + address: [ "172.18.28.0:2379" ] + +serve: + grpc: + address: "172.18.28.0" + port: 8504 + ttl: 20 diff --git a/Server/scene/Dockerfile b/Server/scene/Dockerfile new file mode 100644 index 0000000..2212be1 --- /dev/null +++ b/Server/scene/Dockerfile @@ -0,0 +1,11 @@ +FROM alpine:latest + +RUN apk add --no-cache tzdata && \ + ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ + echo "Asia/Shanghai" > /etc/timezone + +COPY scene/ /app/ +RUN chmod 777 /app/server-scene + +WORKDIR /app +CMD ["./server-scene"] \ No newline at end of file diff --git a/Server/scene/app/app.go b/Server/scene/app/app.go index 66928ba..538f04a 100644 --- a/Server/scene/app/app.go +++ b/Server/scene/app/app.go @@ -1,63 +1,58 @@ package app import ( - "common/db/etcd" "common/discover" "common/log" - "common/net/grpc/service" "fmt" "github.com/judwhite/go-svc" - "runtime/debug" "scene/config" - "scene/grpc_server/server" - "sync" ) type Program struct { - wg *sync.WaitGroup - server service.IService // grpc服务 - stop chan bool + moduleList []Module // 模块列表 +} + +type Module interface { + Init() error + Start() error + Stop() error } func (p *Program) Init(_ svc.Environment) error { - if err := p.initBase(); err != nil { - return err - } - cfg := config.Get() - log.Infof(fmt.Sprintf("%v starting...", cfg.App.Name)) - if err := p.initDB(cfg); err != nil { - return err + p.moduleList = append(p.moduleList, &ModuleBase{}) + p.moduleList = append(p.moduleList, &ModuleDB{}) + p.moduleList = append(p.moduleList, &ModuleGrpcServer{}) + + for _, module := range p.moduleList { + if err := module.Init(); err != nil { + return err + } } + log.Infof(fmt.Sprintf("%v Init successful...", config.Get().App.Name)) return nil } func (p *Program) Start() error { - defer func() { - if err := recover(); err != nil { - fmt.Printf("Start err: %v", err) - debug.PrintStack() - _ = p.Stop() + for _, module := range p.moduleList { + if err := module.Start(); err != nil { + return err } - }() - + } discover.Listen() - p.server = server.NewServer(config.Get().Serve.Grpc.TTL) - p.server.Init(config.Get().Serve.Grpc.Address, config.Get().Serve.Grpc.Port) + log.Infof(fmt.Sprintf("%v Start successful...", config.Get().App.Name)) return nil } func (p *Program) Stop() error { - defer func() { - if err := recover(); err != nil { - fmt.Printf("Stop err: %v", err) - debug.PrintStack() - } - }() - discover.Close() - p.server.Close() - _ = etcd.Close() + for i := len(p.moduleList) - 1; i >= 0; i-- { + module := p.moduleList[i] + if err := module.Stop(); err != nil { + log.Errorf("module stop error: %v", err) + } + } + log.Infof(fmt.Sprintf("%v Stop successful...", config.Get().App.Name)) return nil } diff --git a/Server/scene/app/base.go b/Server/scene/app/base.go index 4d4b91a..31500e4 100644 --- a/Server/scene/app/base.go +++ b/Server/scene/app/base.go @@ -7,7 +7,11 @@ import ( "scene/config" ) -func (p *Program) initBase() error { +// ModuleBase 基础模块,或者一些零散的模块 +type ModuleBase struct { +} + +func (p *ModuleBase) Init() error { // 配置 if err := config.LoadConfig(); err != nil { return err @@ -17,6 +21,13 @@ func (p *Program) initBase() error { log.Init(cfg.Log.Debug, cfg.Log.MaxSize, cfg.Log.MaxBackups, cfg.Log.MaxAge, cfg.Log.Level) // 雪花 utils.InitSnowflake(int64(rand.Intn(1000))) - + return nil +} + +func (p *ModuleBase) Start() error { + return nil +} + +func (p *ModuleBase) Stop() error { return nil } diff --git a/Server/scene/app/db.go b/Server/scene/app/db.go index 03542b4..3cb407b 100644 --- a/Server/scene/app/db.go +++ b/Server/scene/app/db.go @@ -2,10 +2,16 @@ package app import ( "common/db/etcd" + "common/log" "scene/config" ) -func (p *Program) initDB(cfg *config.Config) error { +// ModuleDB 数据库模块 +type ModuleDB struct { +} + +func (p *ModuleDB) Init() error { + cfg := config.Get() // ETCD if err := etcd.Init(cfg.DB.Etcd.Address); err != nil { return err @@ -13,8 +19,13 @@ func (p *Program) initDB(cfg *config.Config) error { return nil } -func (p *Program) stopDB() error { - _ = etcd.Close() - +func (p *ModuleDB) Start() error { + return nil +} + +func (p *ModuleDB) Stop() error { + if err := etcd.Close(); err != nil { + log.Errorf("close etcd failed: %v", err) + } return nil } diff --git a/Server/scene/app/grpc.go b/Server/scene/app/grpc.go new file mode 100644 index 0000000..3bce351 --- /dev/null +++ b/Server/scene/app/grpc.go @@ -0,0 +1,27 @@ +package app + +import ( + "common/net/grpc/service" + "scene/config" + "scene/grpc_server/server" +) + +// ModuleGrpcServer Grpc服务模块 +type ModuleGrpcServer struct { + server service.IService +} + +func (m *ModuleGrpcServer) Init() error { + return nil +} + +func (m *ModuleGrpcServer) Start() error { + m.server = server.NewServer(config.Get().Serve.Grpc.TTL) + m.server.Init(config.Get().Serve.Grpc.Address, config.Get().Serve.Grpc.Port) + return nil +} + +func (m *ModuleGrpcServer) Stop() error { + m.server.Close() + return nil +} diff --git a/Server/scene/config/config.dev.yaml b/Server/scene/config/config.dev.yaml index 9460a4a..7ad3905 100644 --- a/Server/scene/config/config.dev.yaml +++ b/Server/scene/config/config.dev.yaml @@ -4,8 +4,8 @@ app: log: debug: true level: "debug" - maxSize: 100 - maxBackups: 3 + maxSize: 10 + maxBackups: 100 maxAge: 7 db: diff --git a/Server/scene/config/config.prod.yaml b/Server/scene/config/config.prod.yaml new file mode 100644 index 0000000..9b0d7a5 --- /dev/null +++ b/Server/scene/config/config.prod.yaml @@ -0,0 +1,19 @@ +app: + name: "scene-prod" + +log: + debug: false + level: "debug" + maxSize: 10 + maxBackups: 100 + maxAge: 7 + +db: + etcd: + address: [ "172.18.28.0:2379" ] + +serve: + grpc: + address: "172.18.28.0" + port: 8504 + ttl: 20