feat 结构调整
This commit is contained in:
58
Server/gateway/internal/grpc_server/server/server.go
Normal file
58
Server/gateway/internal/grpc_server/server/server.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"common/log"
|
||||
"common/proto/sc/sc_pb"
|
||||
"common/proto/ss/grpc_pb"
|
||||
"gateway/internal/handler/ws_handler"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func (s *Server) ToClient(server grpc_pb.Gateway_ToClientServer) error {
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Errorf("Action panic: %v", err)
|
||||
}
|
||||
}()
|
||||
for {
|
||||
if args, err := server.Recv(); err != nil {
|
||||
return
|
||||
} else {
|
||||
if args.UID == -1 {
|
||||
|
||||
//utils.WorkerPool(ws_handler.UserMgr.GetAllInterface(), func(task interface{}) {
|
||||
// client := task.(*ws_handler.Client)
|
||||
// client.WriteBytes(sc_pb.MessageID(args.MessageID), args.Payload)
|
||||
//})
|
||||
|
||||
data, err := proto.Marshal(&sc_pb.Message{
|
||||
ID: sc_pb.MessageID(args.MessageID),
|
||||
Payload: args.Payload,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("ToClient proto.Marshal error: %v", err)
|
||||
continue
|
||||
}
|
||||
for _, client := range ws_handler.UserMgr.GetAll() {
|
||||
client.WriteBytesPreMarshal(data)
|
||||
}
|
||||
|
||||
//for _, client := range ws_handler.UserMgr.GetAll() {
|
||||
// client.WriteBytes(sc_pb.MessageID(args.MessageID), args.Payload)
|
||||
//}
|
||||
} else {
|
||||
if client := ws_handler.UserMgr.GetByUID(args.UID); client != nil {
|
||||
client.WriteBytes(sc_pb.MessageID(args.MessageID), args.Payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
wg.Wait()
|
||||
return server.SendAndClose(&grpc_pb.ToClientResp{})
|
||||
}
|
||||
34
Server/gateway/internal/grpc_server/server/server_init.go
Normal file
34
Server/gateway/internal/grpc_server/server/server_init.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"common/discover/common"
|
||||
"common/net/grpc/service"
|
||||
"common/proto/ss/grpc_pb"
|
||||
"gateway/internal/handler/ws_handler"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
grpc_pb.UnimplementedGatewayServer
|
||||
service.Base
|
||||
}
|
||||
|
||||
func NewServer(ttl int64) *Server {
|
||||
s := &Server{
|
||||
Base: service.Base{
|
||||
Target: common.KeyDiscoverGateway,
|
||||
EtcdTTL: ttl,
|
||||
},
|
||||
}
|
||||
s.Base.OnInit = s.OnInit
|
||||
s.Base.OnClose = s.OnClose
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Server) OnInit(serve *grpc.Server) {
|
||||
ws_handler.GatewaySID = s.SID
|
||||
grpc_pb.RegisterGatewayServer(serve, s)
|
||||
}
|
||||
|
||||
func (s *Server) OnClose() {
|
||||
}
|
||||
65
Server/gateway/internal/grpc_server/stream_client/scene.go
Normal file
65
Server/gateway/internal/grpc_server/stream_client/scene.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package stream_client
|
||||
|
||||
import (
|
||||
"common/log"
|
||||
"common/net/grpc/service"
|
||||
"context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var sceneServerM map[int64]map[SceneFun]grpc.ClientStream // map[sid]map[方法名]流连接
|
||||
|
||||
type SceneFun int
|
||||
|
||||
const (
|
||||
FunAction SceneFun = iota
|
||||
)
|
||||
|
||||
func init() {
|
||||
sceneServerM = make(map[int64]map[SceneFun]grpc.ClientStream)
|
||||
}
|
||||
|
||||
func findSceneBySID(sid int64, fun SceneFun) (grpc.ClientStream, error) {
|
||||
g := sceneServerM[sid]
|
||||
if g == nil {
|
||||
g = make(map[SceneFun]grpc.ClientStream)
|
||||
sceneServerM[sid] = g
|
||||
}
|
||||
sceneLink := g[fun]
|
||||
if sceneLink == nil {
|
||||
sceneClient, err := service.SceneNewClient(sid)
|
||||
if err != nil {
|
||||
log.Errorf("cannot find sceneClient: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
var link grpc.ClientStream
|
||||
switch fun {
|
||||
case FunAction:
|
||||
link, err = sceneClient.Action(context.Background())
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("findSceneBySID %v err: %v, sid: %v", fun, err, sid)
|
||||
return nil, err
|
||||
}
|
||||
g[fun] = link
|
||||
sceneLink = link
|
||||
}
|
||||
return sceneLink, nil
|
||||
}
|
||||
|
||||
func SendMessageToScene(sid int64, fun SceneFun, msg proto.Message, re ...bool) error {
|
||||
stream, err := findSceneBySID(sid, fun)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = stream.SendMsg(msg); err != nil {
|
||||
if re == nil || !re[0] {
|
||||
_ = stream.CloseSend()
|
||||
delete(sceneServerM[sid], fun)
|
||||
return SendMessageToScene(sid, fun, msg, true)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
105
Server/gateway/internal/handler/ws_handler/client.go
Normal file
105
Server/gateway/internal/handler/ws_handler/client.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package ws_handler
|
||||
|
||||
import (
|
||||
"common/log"
|
||||
"common/net/socket"
|
||||
"context"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var GatewaySID int64
|
||||
|
||||
type Client struct {
|
||||
sync.WaitGroup
|
||||
conn socket.ISocketConn // Socket
|
||||
mailChan chan Event // 邮箱队列
|
||||
logger *zap.SugaredLogger // 日志
|
||||
ctx context.Context // 上下文
|
||||
cancel context.CancelFunc // 取消上下文
|
||||
heartBeat time.Time // 最后一次心跳
|
||||
|
||||
UID int32 // 用户ID
|
||||
SceneSID int64 // 场景服ID
|
||||
InstanceID int32 // 副本ID,副本类型
|
||||
UniqueNo int64 // 副本唯一编号
|
||||
}
|
||||
|
||||
func NewClient(uid int32, conn socket.ISocketConn) *Client {
|
||||
client := &Client{
|
||||
UID: uid,
|
||||
conn: conn,
|
||||
logger: log.GetLogger().Named(fmt.Sprintf("uid:%v", uid)),
|
||||
heartBeat: time.Now(),
|
||||
mailChan: make(chan Event, 1024),
|
||||
}
|
||||
client.ctx, client.cancel = context.WithCancel(context.Background())
|
||||
client.Add(1)
|
||||
go client.Loop()
|
||||
return client
|
||||
}
|
||||
|
||||
func (c *Client) Loop() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
c.logger.Errorf("Client Loop err: %v", err)
|
||||
debug.PrintStack()
|
||||
}
|
||||
}()
|
||||
defer c.onClose()
|
||||
t := time.NewTicker(20 * time.Second)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return
|
||||
case evt, ok := <-c.mailChan:
|
||||
if ok {
|
||||
c.handle(evt)
|
||||
}
|
||||
case <-t.C:
|
||||
_ = c.conn.Ping()
|
||||
if time.Now().Sub(c.heartBeat) > 60*time.Second {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) OnEvent(event Event) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
c.logger.Warnf(fmt.Sprintf("send event chan error: %v", err))
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case c.mailChan <- event:
|
||||
default:
|
||||
c.logger.Warnf("Client mailChan full")
|
||||
}
|
||||
}
|
||||
|
||||
// CloseClient 关闭客户端(同步,会等待onClose执行完成)
|
||||
func (c *Client) CloseClient() {
|
||||
if c.cancel != nil {
|
||||
c.cancel()
|
||||
c.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) onClose() {
|
||||
if c.conn != nil {
|
||||
_ = c.conn.Close()
|
||||
c.conn = nil
|
||||
}
|
||||
if c.mailChan != nil {
|
||||
close(c.mailChan)
|
||||
c.mailChan = nil
|
||||
}
|
||||
UserMgr.Delete(c.UID)
|
||||
c.onLeave()
|
||||
c.Done()
|
||||
}
|
||||
57
Server/gateway/internal/handler/ws_handler/client_write.go
Normal file
57
Server/gateway/internal/handler/ws_handler/client_write.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package ws_handler
|
||||
|
||||
import (
|
||||
"common/proto/sc/sc_pb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// WriteMessage 向客户端发送消息
|
||||
func (c *Client) WriteMessage(id sc_pb.MessageID, data proto.Message) {
|
||||
if c.conn == nil || c.conn.IsClose() {
|
||||
return
|
||||
}
|
||||
d, err := proto.Marshal(data)
|
||||
if err != nil {
|
||||
c.logger.Errorf("WriteMessage proto.Marshal err: %v", err)
|
||||
return
|
||||
}
|
||||
m, err := proto.Marshal(&sc_pb.Message{
|
||||
ID: id,
|
||||
Payload: d,
|
||||
})
|
||||
if err != nil {
|
||||
c.logger.Errorf("WriteMessage proto.Marshal err: %v", err)
|
||||
return
|
||||
}
|
||||
if err = c.conn.Write(m); err != nil {
|
||||
c.logger.Errorf("WriteMessage err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteBytes 向客户端发送字节数据
|
||||
func (c *Client) WriteBytes(id sc_pb.MessageID, data []byte) {
|
||||
if c.conn == nil || c.conn.IsClose() {
|
||||
return
|
||||
}
|
||||
m, err := proto.Marshal(&sc_pb.Message{
|
||||
ID: id,
|
||||
Payload: data,
|
||||
})
|
||||
if err != nil {
|
||||
c.logger.Errorf("WriteBytes proto.Marshal err: %v", err)
|
||||
return
|
||||
}
|
||||
if err = c.conn.Write(m); err != nil {
|
||||
c.logger.Errorf("WriteBytes err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteBytesPreMarshal 向客户端发送字节数据(需要预先打包,适合广播相同数据)
|
||||
func (c *Client) WriteBytesPreMarshal(data []byte) {
|
||||
if c.conn == nil || c.conn.IsClose() {
|
||||
return
|
||||
}
|
||||
if err := c.conn.Write(data); err != nil {
|
||||
c.logger.Errorf("WriteBytes err: %v", err)
|
||||
}
|
||||
}
|
||||
13
Server/gateway/internal/handler/ws_handler/event.go
Normal file
13
Server/gateway/internal/handler/ws_handler/event.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package ws_handler
|
||||
|
||||
type Event interface {
|
||||
}
|
||||
|
||||
type ClientEvent struct {
|
||||
Event
|
||||
Msg []byte
|
||||
}
|
||||
|
||||
type PongEvent struct {
|
||||
Event
|
||||
}
|
||||
97
Server/gateway/internal/handler/ws_handler/handler.go
Normal file
97
Server/gateway/internal/handler/ws_handler/handler.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package ws_handler
|
||||
|
||||
import (
|
||||
"common/net/grpc/service"
|
||||
"common/proto/sc/sc_pb"
|
||||
"common/proto/ss/grpc_pb"
|
||||
"gateway/internal/grpc_server/stream_client"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (c *Client) handle(event Event) {
|
||||
switch e := event.(type) {
|
||||
case *ClientEvent:
|
||||
msg := &sc_pb.Message{}
|
||||
if err := proto.Unmarshal(e.Msg, msg); err != nil {
|
||||
c.logger.Errorf("handle event proto.Unmarshal err: %v", err)
|
||||
c.cancel()
|
||||
return
|
||||
}
|
||||
switch msg.ID {
|
||||
case sc_pb.MessageID_MESSAGE_ID_ENTER_INSTANCE:
|
||||
m := &sc_pb.C2S_EnterInstance{}
|
||||
if err := proto.Unmarshal(msg.Payload, m); err != nil {
|
||||
c.logger.Errorf("handle event proto.Unmarshal err: %v", err)
|
||||
c.cancel()
|
||||
return
|
||||
}
|
||||
c.onEnter(m)
|
||||
case sc_pb.MessageID_MESSAGE_ID_ACTION:
|
||||
m := &sc_pb.C2S_Action{}
|
||||
if err := proto.Unmarshal(msg.Payload, m); err != nil {
|
||||
c.logger.Errorf("handle event proto.Unmarshal err: %v", err)
|
||||
c.cancel()
|
||||
return
|
||||
}
|
||||
c.onAction(m)
|
||||
}
|
||||
case *PongEvent:
|
||||
c.heartBeat = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) onEnter(msg *sc_pb.C2S_EnterInstance) {
|
||||
client, err := service.SceneNewClientLB()
|
||||
if err != nil {
|
||||
c.logger.Errorf("SceneNewClient err: %v", err)
|
||||
return
|
||||
}
|
||||
resp, err := client.Enter(c.ctx, &grpc_pb.EnterReq{
|
||||
UID: c.UID,
|
||||
GatewaySID: GatewaySID,
|
||||
InstanceID: msg.InstanceID,
|
||||
})
|
||||
if err != nil {
|
||||
c.logger.Errorf("enter err: %v", err)
|
||||
return
|
||||
}
|
||||
c.SceneSID = resp.SceneSID
|
||||
c.UniqueNo = resp.UniqueNo
|
||||
c.InstanceID = msg.InstanceID
|
||||
c.WriteBytes(sc_pb.MessageID(resp.MessageID), resp.Payload)
|
||||
}
|
||||
|
||||
func (c *Client) onLeave() {
|
||||
client, err := service.SceneNewClient(c.SceneSID)
|
||||
if err != nil {
|
||||
c.logger.Errorf("SceneNewClient err: %v", err)
|
||||
return
|
||||
}
|
||||
_, err = client.Leave(c.ctx, &grpc_pb.LeaveReq{
|
||||
UID: c.UID,
|
||||
GatewaySID: GatewaySID,
|
||||
InstanceID: c.InstanceID,
|
||||
UniqueNo: c.UniqueNo,
|
||||
})
|
||||
if err != nil {
|
||||
c.logger.Errorf("leave err: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) onAction(msg *sc_pb.C2S_Action) {
|
||||
if c.SceneSID == 0 {
|
||||
return
|
||||
}
|
||||
if err := stream_client.SendMessageToScene(c.SceneSID, stream_client.FunAction, &grpc_pb.ActionReq{
|
||||
UniqueNo: c.UniqueNo,
|
||||
UID: c.UID,
|
||||
Action: int32(msg.Action),
|
||||
DirX: msg.DirX,
|
||||
DirY: msg.DirY,
|
||||
SkillID: msg.SkillID,
|
||||
}); err != nil {
|
||||
c.logger.Errorf("send action err: %v", err)
|
||||
}
|
||||
}
|
||||
58
Server/gateway/internal/handler/ws_handler/manager.go
Normal file
58
Server/gateway/internal/handler/ws_handler/manager.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package ws_handler
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var UserMgr *userManager
|
||||
|
||||
type userManager struct {
|
||||
userMap map[int32]*Client
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func init() {
|
||||
UserMgr = &userManager{
|
||||
userMap: make(map[int32]*Client),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *userManager) Add(uid int32, client *Client) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
m.userMap[uid] = client
|
||||
}
|
||||
|
||||
func (m *userManager) Delete(uid int32) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
delete(m.userMap, uid)
|
||||
}
|
||||
|
||||
func (m *userManager) GetAll() map[int32]*Client {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
|
||||
copyMap := make(map[int32]*Client, len(m.userMap))
|
||||
for k, v := range m.userMap {
|
||||
copyMap[k] = v
|
||||
}
|
||||
return copyMap
|
||||
}
|
||||
|
||||
func (m *userManager) GetAllInterface() []interface{} {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
|
||||
r := make([]interface{}, 0)
|
||||
for _, v := range m.userMap {
|
||||
r = append(r, v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (m *userManager) GetByUID(uid int32) *Client {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
return m.userMap[uid]
|
||||
}
|
||||
37
Server/gateway/internal/net/http_gateway/middleward.go
Normal file
37
Server/gateway/internal/net/http_gateway/middleward.go
Normal file
@@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
54
Server/gateway/internal/net/http_gateway/router.go
Normal file
54
Server/gateway/internal/net/http_gateway/router.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package http_gateway
|
||||
|
||||
import (
|
||||
"common/log"
|
||||
"common/net/http/http_resp"
|
||||
wrapper2 "gateway/internal/net/http_gateway/wrapper"
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func InitServeMux() *runtime.ServeMux {
|
||||
baseMarshaler := &runtime.JSONPb{
|
||||
MarshalOptions: protojson.MarshalOptions{
|
||||
UseProtoNames: false,
|
||||
EmitUnpopulated: true,
|
||||
},
|
||||
UnmarshalOptions: protojson.UnmarshalOptions{
|
||||
DiscardUnknown: true,
|
||||
},
|
||||
}
|
||||
unifiedMarshaler := wrapper2.NewWrappedMarshaler(baseMarshaler)
|
||||
|
||||
mux := runtime.NewServeMux(
|
||||
runtime.WithMarshalerOption(runtime.MIMEWildcard, unifiedMarshaler),
|
||||
runtime.WithErrorHandler(wrapper2.ErrorHandler),
|
||||
)
|
||||
return mux
|
||||
}
|
||||
|
||||
func InitRouter(mux *runtime.ServeMux) *gin.Engine {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
|
||||
r := gin.New()
|
||||
r.Use(
|
||||
gin.Recovery(),
|
||||
ginLogger(log.GetLogger().Named("GIN")),
|
||||
cors.New(corsConfig()),
|
||||
)
|
||||
|
||||
r.HandleMethodNotAllowed = true
|
||||
r.NoMethod(func(c *gin.Context) {
|
||||
c.JSON(http.StatusMethodNotAllowed, http_resp.Error(http_resp.Failed.Code(), "Method Not Allowed"))
|
||||
})
|
||||
r.NoRoute(func(c *gin.Context) {
|
||||
c.JSON(http.StatusNotFound, http_resp.Error(http_resp.Failed.Code(), "Endpoint Not Found"))
|
||||
})
|
||||
|
||||
r.Any("/*any", gin.WrapH(mux))
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"common/net/http/http_resp"
|
||||
"common/proto/ss/ss_common"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ErrorHandler 将 gRPC 错误转为统一 JSON 格式
|
||||
func ErrorHandler(_ context.Context, _ *runtime.ServeMux, _ runtime.Marshaler, w http.ResponseWriter, _ *http.Request, err error) {
|
||||
st, ok := status.FromError(err)
|
||||
if !ok {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_ = json.NewEncoder(w).Encode(http_resp.Error(http_resp.Failed.Code(), http_resp.Failed.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
code, msg := 0, ""
|
||||
for _, detail := range st.Details() {
|
||||
if errorInfo, ok := detail.(*ss_common.ErrorInfo); ok {
|
||||
code = int(errorInfo.Code)
|
||||
msg = errorInfo.Msg
|
||||
break
|
||||
}
|
||||
}
|
||||
if code == 0 {
|
||||
code = http_resp.Failed.Code()
|
||||
msg = http_resp.Failed.Error()
|
||||
}
|
||||
if st.Code() == codes.Unknown {
|
||||
msg = st.Message()
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(grpcCodeToHTTPCode(st.Code()))
|
||||
_ = json.NewEncoder(w).Encode(http_resp.Error(code, msg))
|
||||
}
|
||||
|
||||
// 这里定义 Internal 属于业务错误,其他的属于 500 报错
|
||||
func grpcCodeToHTTPCode(c codes.Code) int {
|
||||
switch c {
|
||||
case codes.OK, codes.Unknown:
|
||||
return http.StatusOK
|
||||
default:
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
}
|
||||
42
Server/gateway/internal/net/http_gateway/wrapper/marshal.go
Normal file
42
Server/gateway/internal/net/http_gateway/wrapper/marshal.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"common/net/http/http_resp"
|
||||
"encoding/json"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"io"
|
||||
)
|
||||
|
||||
// WrappedMarshaler 自动包装响应为 { code, message, data }
|
||||
type WrappedMarshaler struct {
|
||||
inner runtime.Marshaler
|
||||
}
|
||||
|
||||
func NewWrappedMarshaler(inner runtime.Marshaler) *WrappedMarshaler {
|
||||
return &WrappedMarshaler{inner: inner}
|
||||
}
|
||||
|
||||
// Marshal 将 gRPC 响应包装成统一格式
|
||||
func (w *WrappedMarshaler) Marshal(v interface{}) ([]byte, error) {
|
||||
dataBytes, err := w.inner.Marshal(v)
|
||||
if err != nil {
|
||||
return json.Marshal(http_resp.Error(http_resp.Failed.Code(), http_resp.Failed.Error()))
|
||||
}
|
||||
return json.Marshal(http_resp.Success(json.RawMessage(dataBytes)))
|
||||
}
|
||||
|
||||
func (w *WrappedMarshaler) Unmarshal(data []byte, v interface{}) error {
|
||||
return w.inner.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func (w *WrappedMarshaler) NewDecoder(r io.Reader) runtime.Decoder {
|
||||
return w.inner.NewDecoder(r)
|
||||
}
|
||||
|
||||
func (w *WrappedMarshaler) NewEncoder(wr io.Writer) runtime.Encoder {
|
||||
return w.inner.NewEncoder(wr)
|
||||
}
|
||||
|
||||
func (w *WrappedMarshaler) ContentType(v interface{}) string {
|
||||
return "application/json"
|
||||
}
|
||||
64
Server/gateway/internal/net/ws_gateway/server.go
Normal file
64
Server/gateway/internal/net/ws_gateway/server.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package ws_gateway
|
||||
|
||||
import (
|
||||
"common/log"
|
||||
"common/net/socket"
|
||||
"fmt"
|
||||
ws_handler2 "gateway/internal/handler/ws_handler"
|
||||
"go.uber.org/zap"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type GatewayWsServer struct {
|
||||
logger *zap.SugaredLogger
|
||||
}
|
||||
|
||||
func (g *GatewayWsServer) OnOpen(conn socket.ISocketConn) ([]byte, socket.Action) {
|
||||
g.logger = log.GetLogger().Named(fmt.Sprintf("addr:%v", conn.RemoteAddr()))
|
||||
return nil, socket.None
|
||||
}
|
||||
|
||||
func (g *GatewayWsServer) OnHandShake(conn socket.ISocketConn) {
|
||||
token, ok := conn.GetParam("token").(string)
|
||||
if !ok || len(token) == 0 {
|
||||
g.logger.Warnf("token is not string")
|
||||
_ = conn.Close()
|
||||
return
|
||||
}
|
||||
t, err := strconv.Atoi(token)
|
||||
if err != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
if oldClient := ws_handler2.UserMgr.GetByUID(int32(t)); oldClient != nil {
|
||||
oldClient.CloseClient()
|
||||
}
|
||||
client := ws_handler2.NewClient(int32(t), conn)
|
||||
ws_handler2.UserMgr.Add(int32(t), client)
|
||||
conn.SetParam("client", client)
|
||||
}
|
||||
|
||||
func (g *GatewayWsServer) OnMessage(conn socket.ISocketConn, bytes []byte) socket.Action {
|
||||
client, ok := conn.GetParam("client").(*ws_handler2.Client)
|
||||
if !ok || client.UID == 0 {
|
||||
return socket.Close
|
||||
}
|
||||
client.OnEvent(&ws_handler2.ClientEvent{Msg: bytes})
|
||||
return socket.None
|
||||
}
|
||||
|
||||
func (g *GatewayWsServer) OnPong(conn socket.ISocketConn) {
|
||||
client, ok := conn.GetParam("client").(*ws_handler2.Client)
|
||||
if !ok || client.UID == 0 {
|
||||
return
|
||||
}
|
||||
client.OnEvent(&ws_handler2.PongEvent{})
|
||||
}
|
||||
|
||||
func (g *GatewayWsServer) OnClose(_ socket.ISocketConn, _ error) socket.Action {
|
||||
return socket.Close
|
||||
}
|
||||
|
||||
func (g *GatewayWsServer) OnTick() (time.Duration, socket.Action) {
|
||||
return 5 * time.Second, socket.None
|
||||
}
|
||||
Reference in New Issue
Block a user