feat: server 目录中新增 gateway 包,提供了基本的 Websocket 网关实现
This commit is contained in:
parent
322938accf
commit
5ff74b623d
|
@ -0,0 +1,62 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/server"
|
||||||
|
"github.com/kercylan98/minotaur/server/client"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewEndpoint 创建网关端点
|
||||||
|
func NewEndpoint(name, address string) *Endpoint {
|
||||||
|
endpoint := &Endpoint{
|
||||||
|
client: client.NewWebsocket(address),
|
||||||
|
name: name,
|
||||||
|
address: address,
|
||||||
|
}
|
||||||
|
endpoint.client.RegConnectionClosedEvent(endpoint.onConnectionClosed)
|
||||||
|
endpoint.client.RegConnectionReceivePacketEvent(endpoint.onConnectionReceivePacket)
|
||||||
|
return endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoint 网关端点
|
||||||
|
type Endpoint struct {
|
||||||
|
client *client.Websocket // 端点客户端
|
||||||
|
name string // 端点名称
|
||||||
|
address string // 端点地址
|
||||||
|
state float64 // 端点健康值(0为不可用,越高越优)
|
||||||
|
offline bool // 离线
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offline 离线
|
||||||
|
func (slf *Endpoint) Offline() {
|
||||||
|
slf.offline = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect 连接端点
|
||||||
|
func (slf *Endpoint) Connect() {
|
||||||
|
for {
|
||||||
|
var now = time.Now()
|
||||||
|
if err := slf.client.Run(); err == nil {
|
||||||
|
slf.state = 1 - (time.Since(now).Seconds() / 10)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write 写入数据
|
||||||
|
func (slf *Endpoint) Write(packet server.Packet) {
|
||||||
|
slf.client.Write(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *Endpoint) onConnectionClosed(conn *client.Websocket, err any) {
|
||||||
|
if !slf.offline {
|
||||||
|
go slf.Connect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *Endpoint) onConnectionReceivePacket(conn *client.Websocket, packet server.Packet) {
|
||||||
|
p := UnpackGatewayPacket(packet)
|
||||||
|
packet.Data = p.Data
|
||||||
|
conn.GetData(p.ConnID).(*server.Conn).Write(packet)
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/server"
|
||||||
|
"github.com/kercylan98/minotaur/utils/concurrent"
|
||||||
|
"github.com/kercylan98/minotaur/utils/slice"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewEndpointManager 创建网关端点管理器
|
||||||
|
func NewEndpointManager() *EndpointManager {
|
||||||
|
em := &EndpointManager{
|
||||||
|
endpoints: concurrent.NewBalanceMap[string, []*Endpoint](),
|
||||||
|
memory: concurrent.NewBalanceMap[string, *Endpoint](),
|
||||||
|
}
|
||||||
|
return em
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndpointManager 网关端点管理器
|
||||||
|
type EndpointManager struct {
|
||||||
|
endpoints *concurrent.BalanceMap[string, []*Endpoint]
|
||||||
|
memory *concurrent.BalanceMap[string, *Endpoint]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEndpoint 获取端点
|
||||||
|
func (slf *EndpointManager) GetEndpoint(name string, conn *server.Conn) (*Endpoint, error) {
|
||||||
|
endpoint, exist := slf.memory.GetExist(conn.GetID())
|
||||||
|
if exist {
|
||||||
|
return endpoint, nil
|
||||||
|
}
|
||||||
|
slf.endpoints.Atom(func(m map[string][]*Endpoint) {
|
||||||
|
endpoints, exist := m[name]
|
||||||
|
if !exist {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(endpoints) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 随机获取
|
||||||
|
endpoints = slice.Copy(endpoints)
|
||||||
|
slice.Shuffle(endpoints)
|
||||||
|
for _, e := range endpoints {
|
||||||
|
if e.offline || e.state <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
endpoint = e
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if endpoint == nil {
|
||||||
|
return nil, ErrEndpointNotExists
|
||||||
|
}
|
||||||
|
slf.memory.Set(conn.GetID(), endpoint)
|
||||||
|
return endpoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEndpoint 添加端点
|
||||||
|
func (slf *EndpointManager) AddEndpoint(endpoint *Endpoint) error {
|
||||||
|
if endpoint.client.IsConnected() {
|
||||||
|
return ErrCannotAddRunningEndpoint
|
||||||
|
}
|
||||||
|
for _, e := range slf.endpoints.Get(endpoint.name) {
|
||||||
|
if e.address == endpoint.address {
|
||||||
|
return ErrEndpointAlreadyExists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go endpoint.Connect()
|
||||||
|
slf.endpoints.Atom(func(m map[string][]*Endpoint) {
|
||||||
|
m[endpoint.name] = append(m[endpoint.name], endpoint)
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveEndpoint 移除端点
|
||||||
|
func (slf *EndpointManager) RemoveEndpoint(endpoint *Endpoint) error {
|
||||||
|
slf.endpoints.Atom(func(m map[string][]*Endpoint) {
|
||||||
|
var endpoints []*Endpoint
|
||||||
|
endpoints, exist := m[endpoint.name]
|
||||||
|
if !exist {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i, e := range endpoints {
|
||||||
|
if e.address == endpoint.address {
|
||||||
|
endpoints = append(endpoints[:i], endpoints[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m[endpoint.name] = endpoints
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrEndpointAlreadyExists 网关端点已存在
|
||||||
|
ErrEndpointAlreadyExists = errors.New("gateway: endpoint already exists")
|
||||||
|
// ErrCannotAddRunningEndpoint 无法添加一个正在运行的网关端点
|
||||||
|
ErrCannotAddRunningEndpoint = errors.New("gateway: cannot add a running endpoint")
|
||||||
|
// ErrEndpointNotExists 该名称下不存在任何端点
|
||||||
|
ErrEndpointNotExists = errors.New("gateway: endpoint not exists")
|
||||||
|
)
|
|
@ -0,0 +1,71 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/server"
|
||||||
|
"github.com/kercylan98/minotaur/utils/super"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewGateway 基于 server.Server 创建网关服务器
|
||||||
|
func NewGateway(srv *server.Server) *Gateway {
|
||||||
|
gateway := &Gateway{
|
||||||
|
srv: srv,
|
||||||
|
EndpointManager: NewEndpointManager(),
|
||||||
|
}
|
||||||
|
return gateway
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gateway 网关
|
||||||
|
type Gateway struct {
|
||||||
|
*EndpointManager // 端点管理器
|
||||||
|
srv *server.Server // 网关服务器核心
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run 运行网关
|
||||||
|
func (slf *Gateway) Run(addr string) error {
|
||||||
|
slf.srv.RegConnectionOpenedEvent(slf.onConnectionOpened)
|
||||||
|
slf.srv.RegConnectionReceivePacketEvent(slf.onConnectionReceivePacket)
|
||||||
|
return slf.srv.Run(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown 关闭网关
|
||||||
|
func (slf *Gateway) Shutdown() {
|
||||||
|
slf.srv.Shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
// onConnectionOpened 连接打开事件
|
||||||
|
func (slf *Gateway) onConnectionOpened(srv *server.Server, conn *server.Conn) {
|
||||||
|
endpoint, err := slf.GetEndpoint("test", conn)
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
endpoint.client.SetData(conn.GetID(), conn)
|
||||||
|
conn.SetData("endpoint", endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
// onConnectionReceivePacket 连接接收数据包事件
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackGatewayPacket 打包网关数据包
|
||||||
|
func PackGatewayPacket(connID string, websocketType int, data []byte) server.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
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package gateway_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/server"
|
||||||
|
gateway2 "github.com/kercylan98/minotaur/server/gateway"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGateway_RunEndpointServer(t *testing.T) {
|
||||||
|
srv := server.New(server.NetworkWebsocket)
|
||||||
|
srv.RegConnectionReceivePacketEvent(func(srv *server.Server, conn *server.Conn, packet server.Packet) {
|
||||||
|
p := gateway2.UnpackGatewayPacket(packet)
|
||||||
|
fmt.Println("endpoint receive packet", string(p.Data))
|
||||||
|
conn.Write(packet)
|
||||||
|
})
|
||||||
|
if err := srv.Run(":8889"); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGateway_Run(t *testing.T) {
|
||||||
|
srv := server.New(server.NetworkWebsocket)
|
||||||
|
gw := gateway2.NewGateway(srv)
|
||||||
|
srv.RegStartFinishEvent(func(srv *server.Server) {
|
||||||
|
if err := gw.AddEndpoint(gateway2.NewEndpoint("test", "ws://127.0.0.1:8889")); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err := gw.Run(":8888"); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
type Packet struct {
|
||||||
|
ConnID string
|
||||||
|
WebsocketType int
|
||||||
|
Data []byte
|
||||||
|
}
|
Loading…
Reference in New Issue