feat: gateway 数据包支持像普通数据包一样处理,并且支持自定义端点健康评估函数

This commit is contained in:
kercylan98 2023-08-17 18:46:59 +08:00
parent 6e9a578282
commit 351257033e
7 changed files with 96 additions and 38 deletions

View File

@ -3,6 +3,7 @@ package server
import ( import (
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/kercylan98/minotaur/utils/concurrent" "github.com/kercylan98/minotaur/utils/concurrent"
"github.com/kercylan98/minotaur/utils/super"
"github.com/panjf2000/gnet" "github.com/panjf2000/gnet"
"github.com/xtaci/kcp-go/v5" "github.com/xtaci/kcp-go/v5"
"net" "net"
@ -65,6 +66,26 @@ func newWebsocketConn(server *Server, ws *websocket.Conn, ip string) *Conn {
return c return c
} }
// newGatewayConn 创建一个处理网关消息的连接
func newGatewayConn(conn *Conn, connId string) *Conn {
c := &Conn{
server: conn.server,
data: map[any]any{},
}
c.gw = func(packet Packet) {
var gp = GP{
C: connId,
WT: packet.WebsocketType,
D: packet.Data,
T: time.Now().UnixNano(),
}
pd := super.MarshalJSON(&gp)
packet.Data = append(pd, 0xff)
conn.Write(packet)
}
return c
}
// NewEmptyConn 创建一个适用于测试的空连接 // NewEmptyConn 创建一个适用于测试的空连接
func NewEmptyConn(server *Server) *Conn { func NewEmptyConn(server *Server) *Conn {
c := &Conn{ c := &Conn{
@ -88,6 +109,7 @@ type Conn struct {
ws *websocket.Conn ws *websocket.Conn
gn gnet.Conn gn gnet.Conn
kcp *kcp.UDPSession kcp *kcp.UDPSession
gw func(packet Packet)
data map[any]any data map[any]any
mutex sync.Mutex mutex sync.Mutex
packetPool *concurrent.Pool[*connPacket] packetPool *concurrent.Pool[*connPacket]
@ -96,7 +118,7 @@ type Conn struct {
// IsEmpty 是否是空连接 // IsEmpty 是否是空连接
func (slf *Conn) IsEmpty() bool { func (slf *Conn) IsEmpty() bool {
return slf.ws == nil && slf.gn == nil && slf.kcp == nil return slf.ws == nil && slf.gn == nil && slf.kcp == nil && slf.gw == nil
} }
// Reuse 重用连接 // Reuse 重用连接
@ -179,6 +201,10 @@ func (slf *Conn) IsWebsocket() bool {
// Write 向连接中写入数据 // Write 向连接中写入数据
// - messageType: websocket模式中指定消息类型 // - messageType: websocket模式中指定消息类型
func (slf *Conn) Write(packet Packet) { func (slf *Conn) Write(packet Packet) {
if slf.gw != nil {
slf.gw(packet)
return
}
packet = slf.server.OnConnectionWritePacketBeforeEvent(slf, packet) packet = slf.server.OnConnectionWritePacketBeforeEvent(slf, packet)
if slf.packetPool == nil { if slf.packetPool == nil {
return return
@ -194,6 +220,10 @@ func (slf *Conn) Write(packet Packet) {
// WriteWithCallback 与 Write 相同,但是会在写入完成后调用 callback // WriteWithCallback 与 Write 相同,但是会在写入完成后调用 callback
// - 当 callback 为 nil 时,与 Write 相同 // - 当 callback 为 nil 时,与 Write 相同
func (slf *Conn) WriteWithCallback(packet Packet, callback func(err error)) { func (slf *Conn) WriteWithCallback(packet Packet, callback func(err error)) {
if slf.gw != nil {
slf.gw(packet)
return
}
packet = slf.server.OnConnectionWritePacketBeforeEvent(slf, packet) packet = slf.server.OnConnectionWritePacketBeforeEvent(slf, packet)
if slf.packetPool == nil { if slf.packetPool == nil {
return return

View File

@ -3,16 +3,25 @@ package gateway
import ( import (
"github.com/kercylan98/minotaur/server" "github.com/kercylan98/minotaur/server"
"github.com/kercylan98/minotaur/server/client" "github.com/kercylan98/minotaur/server/client"
"github.com/kercylan98/minotaur/utils/super"
"time" "time"
) )
// NewEndpoint 创建网关端点 // NewEndpoint 创建网关端点
func NewEndpoint(name, address string) *Endpoint { func NewEndpoint(name, address string, options ...EndpointOption) *Endpoint {
endpoint := &Endpoint{ endpoint := &Endpoint{
client: client.NewWebsocket(address), client: client.NewWebsocket(address),
name: name, name: name,
address: address, address: address,
} }
for _, option := range options {
option(endpoint)
}
if endpoint.evaluator == nil {
endpoint.evaluator = func(costUnixNano float64) float64 {
return 1 / (1 + 1.5*time.Duration(costUnixNano).Seconds())
}
}
endpoint.client.RegConnectionClosedEvent(endpoint.onConnectionClosed) endpoint.client.RegConnectionClosedEvent(endpoint.onConnectionClosed)
endpoint.client.RegConnectionReceivePacketEvent(endpoint.onConnectionReceivePacket) endpoint.client.RegConnectionReceivePacketEvent(endpoint.onConnectionReceivePacket)
return endpoint return endpoint
@ -25,6 +34,7 @@ type Endpoint struct {
address string // 端点地址 address string // 端点地址
state float64 // 端点健康值0为不可用越高越优 state float64 // 端点健康值0为不可用越高越优
offline bool // 离线 offline bool // 离线
evaluator func(costUnixNano float64) float64 // 端点健康值评估函数
} }
// Offline 离线 // Offline 离线
@ -35,9 +45,9 @@ func (slf *Endpoint) Offline() {
// Connect 连接端点 // Connect 连接端点
func (slf *Endpoint) Connect() { func (slf *Endpoint) Connect() {
for { for {
var now = time.Now() cur := time.Now().UnixNano()
if err := slf.client.Run(); err == nil { if err := slf.client.Run(); err == nil {
slf.state = 1 - (time.Since(now).Seconds() / 10) slf.state = slf.evaluator(float64(time.Now().UnixNano() - cur))
break break
} }
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
@ -56,9 +66,13 @@ func (slf *Endpoint) onConnectionClosed(conn *client.Websocket, err any) {
} }
} }
// onConnectionReceivePacket 解说到来自端点的数据包事件 // onConnectionReceivePacket 接收到来自端点的数据包事件
func (slf *Endpoint) onConnectionReceivePacket(conn *client.Websocket, packet server.Packet) { func (slf *Endpoint) onConnectionReceivePacket(conn *client.Websocket, packet server.Packet) {
p := UnpackGatewayPacket(packet) var gp server.GP
packet.Data = p.Data if err := super.UnmarshalJSON(packet.Data[:len(packet.Data)-1], &gp); err != nil {
conn.GetData(p.ConnID).(*server.Conn).Write(packet) panic(err)
}
cur := time.Now().UnixNano()
slf.state = slf.evaluator(float64(cur - gp.T))
conn.GetData(gp.C).(*server.Conn).Write(server.NewWSPacket(gp.WT, gp.D))
} }

View File

@ -0,0 +1,11 @@
package gateway
// EndpointOption 网关端点选项
type EndpointOption func(endpoint *Endpoint)
// WithEndpointStateEvaluator 设置端点健康值评估函数
func WithEndpointStateEvaluator(evaluator func(costUnixNano float64) float64) EndpointOption {
return func(endpoint *Endpoint) {
endpoint.evaluator = evaluator
}
}

View File

@ -48,27 +48,12 @@ func (slf *Gateway) onConnectionOpened(srv *server.Server, conn *server.Conn) {
// onConnectionReceivePacket 连接接收数据包事件 // onConnectionReceivePacket 连接接收数据包事件
func (slf *Gateway) onConnectionReceivePacket(srv *server.Server, conn *server.Conn, packet server.Packet) { func (slf *Gateway) onConnectionReceivePacket(srv *server.Server, conn *server.Conn, packet server.Packet) {
conn.GetData("endpoint").(*Endpoint).Write(PackGatewayPacket(conn.GetID(), packet.WebsocketType, packet.Data)) var gp = server.GP{
C: conn.GetID(),
WT: packet.WebsocketType,
D: packet.Data,
} }
pd := super.MarshalJSON(&gp)
// PackGatewayPacket 打包网关数据包 packet.Data = append(pd, 0xff)
func PackGatewayPacket(connID string, websocketType int, data []byte) server.Packet { conn.GetData("endpoint").(*Endpoint).Write(packet)
var gatewayPacket = Packet{
ConnID: connID,
WebsocketType: websocketType,
Data: data,
}
return server.Packet{
WebsocketType: websocketType,
Data: super.MarshalJSON(&gatewayPacket),
}
}
// UnpackGatewayPacket 解包网关数据包
func UnpackGatewayPacket(packet server.Packet) Packet {
var gatewayPacket Packet
if err := super.UnmarshalJSON(packet.Data, &gatewayPacket); err != nil {
panic(err)
}
return gatewayPacket
} }

View File

@ -10,8 +10,7 @@ import (
func TestGateway_RunEndpointServer(t *testing.T) { func TestGateway_RunEndpointServer(t *testing.T) {
srv := server.New(server.NetworkWebsocket) srv := server.New(server.NetworkWebsocket)
srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet server.Packet) { srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet server.Packet) {
p := gateway2.UnpackGatewayPacket(packet) fmt.Println("endpoint receive packet", string(packet.Data))
fmt.Println("endpoint receive packet", string(p.Data))
conn.Write(packet) conn.Write(packet)
}) })
if err := srv.Run(":8889"); err != nil { if err := srv.Run(":8889"); err != nil {

8
server/gp.go Normal file
View File

@ -0,0 +1,8 @@
package server
type GP struct {
C string // 连接 ID
WT int // WebSocket 类型
D []byte // 数据
T int64 // 时间戳
}

View File

@ -631,8 +631,19 @@ func (slf *Server) dispatchMessage(msg *Message) {
var conn = attrs[0].(*Conn) var conn = attrs[0].(*Conn)
var packet = attrs[1].([]byte) var packet = attrs[1].([]byte)
var wst = int(packet[len(packet)-1]) var wst = int(packet[len(packet)-1])
var ct = packet[len(packet)-2]
if ct == 0xff {
var gp GP
if err := super.UnmarshalJSON(packet[:len(packet)-2], &gp); err != nil {
panic(err)
}
packet = gp.D
conn = newGatewayConn(conn, gp.C)
} else {
packet = packet[:len(packet)-1]
}
if !slf.OnConnectionPacketPreprocessEvent(conn, packet, func(newPacket []byte) { packet = newPacket }) { if !slf.OnConnectionPacketPreprocessEvent(conn, packet, func(newPacket []byte) { packet = newPacket }) {
slf.OnConnectionReceivePacketEvent(conn, Packet{Data: packet[:len(packet)-1], WebsocketType: wst}) slf.OnConnectionReceivePacketEvent(conn, Packet{Data: packet, WebsocketType: wst})
} }
case MessageTypeError: case MessageTypeError:
err, action := attrs[0].(error), attrs[1].(MessageErrorAction) err, action := attrs[0].(error), attrs[1].(MessageErrorAction)