feat: 服务器支持通过 server.WithDiversion 可选项对数据包消息进行分流处理

适用于类似房间这样的模式中,每个房间的消息将不会对其他房间消息造成阻塞
This commit is contained in:
kercylan98 2023-07-01 12:25:39 +08:00
parent 01bafe6fc0
commit 73cefc9b48
3 changed files with 49 additions and 16 deletions

13
server/conn_readonly.go Normal file
View File

@ -0,0 +1,13 @@
package server
import (
"net"
)
type ConnReadonly interface {
RemoteAddr() net.Addr
GetID() string
GetIP() string // GetData 获取连接数据
GetData(key any) any
IsWebsocket() bool
}

View File

@ -24,6 +24,16 @@ const (
type Option func(srv *Server)
// WithDiversion 通过分流的方式创建服务器
// - diversion分流函数返回一个函数通道用于接收分流的消息
// - 需要确保能够通过 conn 和 packet 确定分流通道
// - 多核模式下将导致消息顺序不一致,通过结果依然是单核处理的,因为分流通道仅有一个
func WithDiversion(diversion func(conn ConnReadonly, packet []byte) chan func()) Option {
return func(srv *Server) {
srv.diversion = diversion
}
}
// WithTicker 通过定时器创建服务器,为服务器添加定时器功能
// - autonomy定时器是否独立运行独立运行的情况下不会作为服务器消息运行会导致并发问题
// - 多核与分流情况下需要考虑是否有必要 autonomy

View File

@ -67,6 +67,7 @@ type Server struct {
isRunning bool // 是否正在运行
isShutdown atomic.Bool // 是否已关闭
closeChannel chan struct{} // 关闭信号
diversion func(conn ConnReadonly, packet []byte) chan func() // 分流器
gServer *gNet // TCP或UDP模式下的服务器
messagePool *synchronization.Pool[*Message] // 消息池
@ -129,7 +130,7 @@ func (slf *Server) Run(addr string) error {
messageChannel := messageChannel
go func() {
for message := range messageChannel {
slf.dispatchMessage(message)
slf.dispatchMessage(message, slf.diversion != nil)
}
}()
}
@ -456,7 +457,16 @@ func (slf *Server) PushCrossMessage(crossName string, serverId int64, packet []b
}
// dispatchMessage 消息分发
func (slf *Server) dispatchMessage(msg *Message) {
func (slf *Server) dispatchMessage(msg *Message, isRedirect bool) {
if slf.diversion != nil && isRedirect && msg.t == MessageTypePacket {
conn, packet, _ := msg.t.deconstructWebSocketPacket(msg.attrs...)
if redirect := slf.diversion(conn, packet); redirect != nil {
redirect <- func() {
slf.dispatchMessage(msg, false)
}
}
return
}
present := time.Now()
defer func() {
if err := recover(); err != nil {