vRp.CD2g_test/server/internal/v2/reactor/reactor.go

215 lines
7.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package reactor
import (
"fmt"
"github.com/kercylan98/minotaur/server/internal/v2/loadbalancer"
"github.com/kercylan98/minotaur/server/internal/v2/queue"
"github.com/kercylan98/minotaur/utils/log/v2"
"github.com/kercylan98/minotaur/utils/super"
"runtime"
"runtime/debug"
"sync"
"sync/atomic"
)
const (
statusNone = iota - 1 // 事件循环未运行
statusRunning // 事件循环运行中
statusClosing // 事件循环关闭中
statusClosed // 事件循环已关闭
)
// NewReactor 创建一个新的 Reactor 实例,初始化系统级别的队列和多个 Socket 对应的队列
func NewReactor[M queue.Message](systemQueueSize, queueSize, systemBufferSize, queueBufferSize int, handler MessageHandler[M], errorHandler ErrorHandler[M]) *Reactor[M] {
if handler == nil {
}
r := &Reactor[M]{
systemQueue: queue.New[int, string, M](-1, systemQueueSize, systemBufferSize),
lb: loadbalancer.NewRoundRobin[int, *queue.Queue[int, string, M]](),
errorHandler: errorHandler,
queueSize: queueSize,
queueBufferSize: queueBufferSize,
state: statusNone,
handler: handler,
location: make(map[string]int),
}
r.logger.Store(log.GetLogger())
defaultNum := runtime.NumCPU()
r.queueRW.Lock()
for i := 0; i < defaultNum; i++ {
r.noneLockAddQueue()
}
r.queueRW.Unlock()
return r
}
// Reactor 是一个消息反应器,管理系统级别的队列和多个 Socket 对应的队列
type Reactor[M queue.Message] struct {
logger atomic.Pointer[log.Logger] // 日志记录器
state int32 // 状态
systemQueue *queue.Queue[int, string, M] // 系统级别的队列
queueSize int // 队列管道大小
queueBufferSize int // 队列缓冲区大小
queues []*queue.Queue[int, string, M] // 所有使用的队列
queueRW sync.RWMutex // 队列读写锁
location map[string]int // 所在队列 ID 映射
locationRW sync.RWMutex // 所在队列 ID 映射锁
lb *loadbalancer.RoundRobin[int, *queue.Queue[int, string, M]] // 负载均衡器
wg sync.WaitGroup // 等待组
cwg sync.WaitGroup // 关闭等待组
handler MessageHandler[M] // 消息处理器
errorHandler ErrorHandler[M] // 错误处理器
}
// SetLogger 设置日志记录器
func (r *Reactor[M]) SetLogger(logger *log.Logger) {
r.logger.Store(logger)
}
// GetLogger 获取日志记录器
func (r *Reactor[M]) GetLogger() *log.Logger {
return r.logger.Load()
}
// process 消息处理
func (r *Reactor[M]) process(msg queue.MessageWrapper[int, string, M]) {
defer func(msg queue.MessageWrapper[int, string, M]) {
if err := super.RecoverTransform(recover()); err != nil {
if r.errorHandler != nil {
r.errorHandler(msg, err)
} else {
r.GetLogger().Error("Reactor", log.String("action", "process"), log.Any("ident", msg.Ident()), log.Int("queue", msg.Queue().Id()), log.Err(err))
debug.PrintStack()
}
}
}(msg)
r.handler(msg)
}
// SystemDispatch 将消息分发到系统级别的队列
func (r *Reactor[M]) SystemDispatch(msg M) error {
if atomic.LoadInt32(&r.state) > statusClosing {
r.queueRW.RUnlock()
return fmt.Errorf("reactor closing or closed")
}
return r.systemQueue.Push(false, "", msg)
}
// IdentDispatch 将消息分发到 ident 使用的队列,当 ident 首次使用时,将会根据负载均衡策略选择一个队列
// - 设置 count 会增加消息的外部计数,当 Reactor 关闭时会等待外部计数归零
func (r *Reactor[M]) IdentDispatch(ident string, msg M) error {
r.queueRW.RLock()
if atomic.LoadInt32(&r.state) > statusClosing {
r.queueRW.RUnlock()
return fmt.Errorf("reactor closing or closed")
}
var next *queue.Queue[int, string, M]
r.locationRW.RLock()
i, exist := r.location[ident]
r.locationRW.RUnlock()
if !exist {
r.locationRW.Lock()
if i, exist = r.location[ident]; !exist {
next = r.lb.Next()
r.location[ident] = next.Id()
r.logger.Load().Debug("Reactor", log.String("action", "bind"), log.Any("ident", ident), log.Any("queue", next.Id()))
} else {
next = r.queues[i]
}
r.locationRW.Unlock()
} else {
next = r.queues[i]
}
r.queueRW.RUnlock()
return next.Push(true, ident, msg)
}
// Run 启动 Reactor运行系统级别的队列和多个 Socket 对应的队列
func (r *Reactor[M]) Run(callbacks ...func(queues []*queue.Queue[int, string, M])) {
if !atomic.CompareAndSwapInt32(&r.state, statusNone, statusRunning) {
return
}
r.queueRW.Lock()
queues := append([]*queue.Queue[int, string, M]{r.systemQueue}, r.queues...)
for _, q := range queues {
r.runQueue(q)
}
r.queueRW.Unlock()
for _, callback := range callbacks {
callback(queues)
}
r.wg.Wait()
}
func (r *Reactor[M]) noneLockAddQueue() {
q := queue.New[int, string, M](len(r.queues), r.queueSize, r.queueBufferSize)
r.lb.Add(q) // 运行前添加到负载均衡器,未运行时允许接收消息
r.queues = append(r.queues, q)
}
func (r *Reactor[M]) noneLockDelQueue(q *queue.Queue[int, string, M]) {
idx := q.Id()
if idx < 0 || idx >= len(r.queues) || r.queues[idx] != q {
return
}
r.queues = append(r.queues[:idx], r.queues[idx+1:]...)
for i := idx; i < len(r.queues); i++ {
r.queues[i].SetId(i)
}
}
func (r *Reactor[M]) runQueue(q *queue.Queue[int, string, M]) {
r.wg.Add(1)
q.SetClosedHandler(func(q *queue.Queue[int, string, M]) {
// 关闭时正在等待关闭完成,外部已加锁,无需再次加锁
r.noneLockDelQueue(q)
r.cwg.Done()
r.logger.Load().Debug("Reactor", log.String("action", "close"), log.Any("queue", q.Id()))
})
go q.Run()
go func(r *Reactor[M], q *queue.Queue[int, string, M]) {
defer r.wg.Done()
for m := range q.Read() {
m(r.process, func(m queue.MessageWrapper[int, string, M], last bool) {
if last {
r.locationRW.RLock()
mq, exist := r.location[m.Ident()]
r.locationRW.RUnlock()
if exist {
r.locationRW.Lock()
defer r.locationRW.Unlock()
mq, exist = r.location[m.Ident()]
if exist {
delete(r.location, m.Ident())
r.queueRW.RLock()
mq := r.queues[mq]
r.queueRW.RUnlock()
r.logger.Load().Debug("Reactor", log.String("action", "unbind"), log.Any("ident", m.Ident()), log.Any("queue", mq.Id()))
}
}
}
})
}
}(r, q)
}
func (r *Reactor[M]) Close() {
if !atomic.CompareAndSwapInt32(&r.state, statusRunning, statusClosing) {
return
}
r.queueRW.Lock()
r.cwg.Add(len(r.queues) + 1)
for _, q := range append(r.queues, r.systemQueue) {
q.Close()
}
r.cwg.Wait()
atomic.StoreInt32(&r.state, statusClosed)
r.queueRW.Unlock()
}