|
|
|
@ -2,10 +2,13 @@ package gnet_server |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"context" |
|
|
|
"context" |
|
|
|
"flag" |
|
|
|
|
|
|
|
"fmt" |
|
|
|
"fmt" |
|
|
|
"github.com/gobwas/ws" |
|
|
|
"github.com/gobwas/ws" |
|
|
|
"log" |
|
|
|
"google.golang.org/protobuf/proto" |
|
|
|
|
|
|
|
"regexp" |
|
|
|
|
|
|
|
"sonet/api/gen/auth" |
|
|
|
|
|
|
|
"sonet/api/gen/postal" |
|
|
|
|
|
|
|
"sonet/pkg/grpc/generic" |
|
|
|
"sonet/pkg/protocol" |
|
|
|
"sonet/pkg/protocol" |
|
|
|
"sonet/pkg/protocol/session" |
|
|
|
"sonet/pkg/protocol/session" |
|
|
|
"sonet/pkg/utils/logger" |
|
|
|
"sonet/pkg/utils/logger" |
|
|
|
@ -16,6 +19,11 @@ import ( |
|
|
|
"github.com/panjf2000/gnet/v2/pkg/logging" |
|
|
|
"github.com/panjf2000/gnet/v2/pkg/logging" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type gnetRequest struct { |
|
|
|
|
|
|
|
conn *gnetConn |
|
|
|
|
|
|
|
payload *protocol.Payload |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type GnetWsServer struct { |
|
|
|
type GnetWsServer struct { |
|
|
|
gnet.BuiltinEventEngine |
|
|
|
gnet.BuiltinEventEngine |
|
|
|
|
|
|
|
|
|
|
|
@ -23,17 +31,28 @@ type GnetWsServer struct { |
|
|
|
multicore bool |
|
|
|
multicore bool |
|
|
|
eng gnet.Engine |
|
|
|
eng gnet.Engine |
|
|
|
connected int64 |
|
|
|
connected int64 |
|
|
|
requests chan *protocol.Payload |
|
|
|
|
|
|
|
|
|
|
|
grpcFactory *generic.GrpcGenericClientFactory |
|
|
|
|
|
|
|
sessionStore session.Store // <string, *session.NetSubject> 当前连接用户,内存缓存
|
|
|
|
|
|
|
|
requests chan *gnetRequest |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func NewGnetWsServer() *GnetWsServer { |
|
|
|
func NewGnetWsServer( |
|
|
|
|
|
|
|
grpcFactory *generic.GrpcGenericClientFactory, |
|
|
|
|
|
|
|
sessionStore session.Store) *GnetWsServer { |
|
|
|
return &GnetWsServer{ |
|
|
|
return &GnetWsServer{ |
|
|
|
requests: make(chan *protocol.Payload, 1024*16), |
|
|
|
grpcFactory: grpcFactory, |
|
|
|
|
|
|
|
sessionStore: sessionStore, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (wss *GnetWsServer) Init() { |
|
|
|
func (wss *GnetWsServer) Init(ctx context.Context) { |
|
|
|
|
|
|
|
wss.requests = make(chan *gnetRequest, 1024*16) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// todo 针对不同服务的协程池
|
|
|
|
|
|
|
|
for i := 0; i < 32; i++ { |
|
|
|
|
|
|
|
go wss.dispatch(ctx) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// dispatch 多个 goroutine 进行请求 dispatch
|
|
|
|
// dispatch 多个 goroutine 进行请求 dispatch
|
|
|
|
@ -42,8 +61,107 @@ func (wss *GnetWsServer) dispatch(ctx context.Context) { |
|
|
|
select { |
|
|
|
select { |
|
|
|
case <-ctx.Done(): |
|
|
|
case <-ctx.Done(): |
|
|
|
return |
|
|
|
return |
|
|
|
case payload := <-wss.requests: |
|
|
|
case request := <-wss.requests: |
|
|
|
fmt.Println(payload.Header) |
|
|
|
func() { |
|
|
|
|
|
|
|
conn, payload := request.conn, request.payload |
|
|
|
|
|
|
|
header := payload.Header |
|
|
|
|
|
|
|
service, method := header.Svc, header.Target |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// grpc generic call
|
|
|
|
|
|
|
|
ctx := context.Background() |
|
|
|
|
|
|
|
ctx2, cancel2 := context.WithTimeout(ctx, time.Second*3) |
|
|
|
|
|
|
|
defer cancel2() |
|
|
|
|
|
|
|
grpcClient, err := wss.grpcFactory.GetClient(ctx2, service) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
logger.Error("get grpc generic client error: ", err) |
|
|
|
|
|
|
|
writeError(conn, header.SeqId, fmt.Sprintf("service %s not avaliable: %s", header.Svc, err.Error())) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// put grpc request session
|
|
|
|
|
|
|
|
if conn.uid != "" { |
|
|
|
|
|
|
|
ctx = session.PutSubject(ctx, session.NewRpcSubject(conn.uid)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ctx3, cancel3 := context.WithTimeout(ctx, time.Second*10) |
|
|
|
|
|
|
|
defer cancel3() |
|
|
|
|
|
|
|
resp, err := grpcClient.InvokeUnary(ctx3, header.Target, payload.Body) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
writeError(conn, header.SeqId, unwrapRpcError(err.Error())) |
|
|
|
|
|
|
|
logger.Error("grpc generic call error: ", err) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// write response
|
|
|
|
|
|
|
|
if resp == nil { // proto.Empty
|
|
|
|
|
|
|
|
header.Target = "" |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
payload.Body, err = resp.Marshal() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
logger.Error("generic call response marshal error: ", err) |
|
|
|
|
|
|
|
writeError(conn, header.SeqId, "server error") |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
header.Target = resp.XXX_MessageName() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// auth verify success
|
|
|
|
|
|
|
|
if service == "Auth" && method == "Verify" { |
|
|
|
|
|
|
|
var channel *session.Channel |
|
|
|
|
|
|
|
conn.uid, channel, err = wss.extractAuthVerify(conn, payload.Body) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
logger.Error("extractAuthVerify error: ", err) |
|
|
|
|
|
|
|
writeError(conn, header.SeqId, "server error") |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// channel
|
|
|
|
|
|
|
|
go wss.processWriting(channel) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
header.Type = protocol.TypeResponse |
|
|
|
|
|
|
|
resMessage, err := protocol.EncodeSo(payload) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
logger.Error("grpc generic call error: ", err) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
err = conn.Write(resMessage) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
logger.Error("gws write response error: ", err) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func encodeDeliverMessage(msg *postal.Message) (bytes []byte, err error) { |
|
|
|
|
|
|
|
header := &protocol.Header{ |
|
|
|
|
|
|
|
Magic: protocol.Magic, |
|
|
|
|
|
|
|
Type: protocol.TypeNotice, |
|
|
|
|
|
|
|
UrlType: 1, |
|
|
|
|
|
|
|
SerializeType: 1, |
|
|
|
|
|
|
|
Svc: msg.Svc, |
|
|
|
|
|
|
|
Target: msg.Msg, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
payload := &protocol.Payload{Header: header, Body: msg.Body} |
|
|
|
|
|
|
|
bytes, err = protocol.EncodeSo(payload) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
logger.Error("encode deliver message error: ", err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// todo close
|
|
|
|
|
|
|
|
func (c *GnetWsServer) processWriting(channel *session.Channel) { |
|
|
|
|
|
|
|
for { |
|
|
|
|
|
|
|
msg := channel.Ready() |
|
|
|
|
|
|
|
bytes, err := encodeDeliverMessage(msg) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
logger.Error("encode dispatch message error:", err) |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
err = channel.Conn.Write(bytes) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
logger.Error("write dispatch message error:", err) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -120,8 +238,9 @@ func (wss *GnetWsServer) OnTraffic(c gnet.Conn) (action gnet.Action) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// payload -> processing channel
|
|
|
|
// payload -> processing channel
|
|
|
|
|
|
|
|
request := &gnetRequest{conn: conn, payload: payload} |
|
|
|
select { |
|
|
|
select { |
|
|
|
case wss.requests <- payload: |
|
|
|
case wss.requests <- request: |
|
|
|
default: |
|
|
|
default: |
|
|
|
writeError(conn, header.SeqId, "server busy") |
|
|
|
writeError(conn, header.SeqId, "server busy") |
|
|
|
} |
|
|
|
} |
|
|
|
@ -146,6 +265,31 @@ func (wss *GnetWsServer) OnTick() (delay time.Duration, action gnet.Action) { |
|
|
|
return 10 * time.Second, gnet.None |
|
|
|
return 10 * time.Second, gnet.None |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (c *GnetWsServer) extractAuthVerify(conn *gnetConn, resp []byte) (sessionUid string, channel *session.Channel, err error) { |
|
|
|
|
|
|
|
authSubject := &auth.Subject{} |
|
|
|
|
|
|
|
err = proto.Unmarshal(resp, authSubject) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
logger.Error("grpc generic call error: ", err) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// 设置连接身份信息
|
|
|
|
|
|
|
|
sessionUid = authSubject.Uid |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭旧的链接
|
|
|
|
|
|
|
|
oldChannel, ok := c.sessionStore.Load(sessionUid) |
|
|
|
|
|
|
|
if ok { |
|
|
|
|
|
|
|
if err = oldChannel.Conn.(*gnetConn).conn.Close(); err != nil { |
|
|
|
|
|
|
|
logger.Error("close gnet conn error: ", err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
logger.Infof("close subject old conn: %s\n", sessionUid) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// 存储session到内存
|
|
|
|
|
|
|
|
channel = session.NewChannel(sessionUid, conn) |
|
|
|
|
|
|
|
c.sessionStore.Store(sessionUid, channel) |
|
|
|
|
|
|
|
logger.Infof("subject online: %s\n", sessionUid) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func writeError(conn session.NetConn, seqId int32, errMsg string) { |
|
|
|
func writeError(conn session.NetConn, seqId int32, errMsg string) { |
|
|
|
header := &protocol.Header{} |
|
|
|
header := &protocol.Header{} |
|
|
|
payload := &protocol.Payload{Header: header} |
|
|
|
payload := &protocol.Payload{Header: header} |
|
|
|
@ -168,17 +312,13 @@ func writeError(conn session.NetConn, seqId int32, errMsg string) { |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func main() { |
|
|
|
var reg = regexp.MustCompile("^rpc error:.*?desc ?= ?(.*)$") |
|
|
|
var port int |
|
|
|
|
|
|
|
var multicore bool |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Example command: go run main.go --port 8080 --multicore=true
|
|
|
|
|
|
|
|
flag.IntVar(&port, "port", 9080, "server port") |
|
|
|
|
|
|
|
flag.BoolVar(&multicore, "multicore", true, "multicore") |
|
|
|
|
|
|
|
flag.Parse() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
wss := &GnetWsServer{addr: fmt.Sprintf("tcp://127.0.0.1:%d", port), multicore: multicore} |
|
|
|
// unwrapRpcError 提取 grpc err: fmt.Sprintf("rpc error: code = %s desc = %s", s.Code(), s.Message())
|
|
|
|
|
|
|
|
func unwrapRpcError(err string) string { |
|
|
|
// Start serving!
|
|
|
|
finds := reg.FindStringSubmatch(err) |
|
|
|
log.Println("server exits:", gnet.Run(wss, wss.addr, gnet.WithMulticore(multicore), gnet.WithReusePort(true), gnet.WithTicker(true))) |
|
|
|
if len(finds) > 1 { |
|
|
|
|
|
|
|
return finds[1] |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|