feat: 新版 server 包 websocket 基础实现、actor 模型实现

This commit is contained in:
kercylan98
2024-03-21 19:56:33 +08:00
parent b2c0bb0da3
commit ef1bb321d7
20 changed files with 781 additions and 248 deletions

33
utils/super/counter.go Normal file
View File

@@ -0,0 +1,33 @@
package super
import (
"github.com/kercylan98/minotaur/utils/generic"
"sync"
)
type Counter[T generic.Number] struct {
v T
p *Counter[T]
rw sync.RWMutex
}
func (c *Counter[T]) Sub() *Counter[T] {
return &Counter[T]{
p: c,
}
}
func (c *Counter[T]) Add(delta T) {
c.rw.Lock()
c.v += delta
c.rw.RUnlock()
if c.p != nil {
c.p.Add(delta)
}
}
func (c *Counter[T]) Val() T {
c.rw.RLock()
defer c.rw.RUnlock()
return c.v
}

View File

@@ -21,3 +21,15 @@ func HandleV[V any](v V, f func(v V)) {
f(v)
}
}
// SafeExec 安全的执行函数,当 f 为 nil 时,不执行,当 f 执行出错时,不会 panic而是转化为 error 进行返回
func SafeExec(f func()) (err error) {
if f == nil {
return
}
defer func() {
err = RecoverTransform(recover())
}()
f()
return
}

View File

@@ -0,0 +1,8 @@
package super
import "io"
type ReadWriter struct {
io.Reader
io.Writer
}

View File

@@ -0,0 +1,78 @@
package unbounded
import (
"sync"
)
// NewChannelBacklog 创建一个并发安全的,基于 channel 和缓冲队列实现的无界缓冲区
//
// 该缓冲区来源于 gRPC 的实现,用于在不使用额外 goroutine 的情况下实现无界缓冲区
// - 该缓冲区的所有方法都是线程安全的,除了用于同步的互斥锁外,不会阻塞任何东西
func NewChannelBacklog[V any]() *ChannelBacklog[V] {
return &ChannelBacklog[V]{c: make(chan V, 1)}
}
// ChannelBacklog 是并发安全的无界缓冲区的实现
type ChannelBacklog[V any] struct {
c chan V
closed bool
mu sync.Mutex
backlog []V
}
// Put 将数据放入缓冲区
func (cb *ChannelBacklog[V]) Put(t V) {
cb.mu.Lock()
defer cb.mu.Unlock()
if cb.closed {
return
}
if len(cb.backlog) == 0 {
select {
case cb.c <- t:
return
default:
}
}
cb.backlog = append(cb.backlog, t)
}
// Load 将缓冲区中的数据发送到读取通道中,如果缓冲区中没有数据,则不会发送
// - 在每次 Get 后都应该执行该函数
func (cb *ChannelBacklog[V]) Load() {
cb.mu.Lock()
defer cb.mu.Unlock()
if cb.closed {
return
}
if len(cb.backlog) > 0 {
select {
case cb.c <- cb.backlog[0]:
cb.backlog = cb.backlog[1:]
default:
}
}
}
// Get 获取可接收消息的读取通道,需要注意的是,每次读取成功都应该通过 Load 函数将缓冲区中的数据加载到读取通道中
func (cb *ChannelBacklog[V]) Get() <-chan V {
return cb.c
}
// Close 关闭
func (cb *ChannelBacklog[V]) Close() {
cb.mu.Lock()
defer cb.mu.Unlock()
if cb.closed {
return
}
cb.closed = true
close(cb.c)
}
// IsClosed 是否已关闭
func (cb *ChannelBacklog[V]) IsClosed() bool {
cb.mu.Lock()
defer cb.mu.Unlock()
return cb.closed
}

94
utils/unbounded/ring.go Normal file
View File

@@ -0,0 +1,94 @@
package unbounded
import (
"github.com/kercylan98/minotaur/utils/buffer"
"github.com/pkg/errors"
"golang.org/x/net/context"
"sync"
)
// NewRing 创建一个并发安全的基于环形缓冲区实现的无界缓冲区
func NewRing[T any](ctx context.Context) *Ring[T] {
r := &Ring[T]{
ctx: ctx,
ring: buffer.NewRing[T](1024),
ch: make(chan T, 1024),
}
r.cond = sync.NewCond(&r.rw)
go r.run()
return r
}
// Ring 是并发安全的,基于环形缓冲区实现的无界缓冲区
type Ring[T any] struct {
ctx context.Context
ring *buffer.Ring[T]
rw sync.RWMutex
cond *sync.Cond
ch chan T
closed bool
}
// Put 将数据放入缓冲区
func (r *Ring[T]) Put(v ...T) error {
if len(v) == 0 {
return nil
}
r.rw.Lock()
if r.closed {
r.rw.Unlock()
return errors.New("unbounded ring is closed")
}
for _, t := range v {
r.ring.Write(t)
}
r.rw.Unlock()
r.cond.Signal()
return nil
}
// Get 获取可接收消息的读取通道
func (r *Ring[T]) Get() <-chan T {
return r.ch
}
// Close 关闭缓冲区
func (r *Ring[T]) Close() {
r.rw.RLock()
r.closed = true
r.rw.RUnlock()
r.cond.Signal()
}
// IsClosed 是否已关闭
func (r *Ring[T]) IsClosed() bool {
r.rw.RLock()
defer r.rw.RUnlock()
return r.closed
}
func (r *Ring[T]) run() {
for {
select {
case <-r.ctx.Done():
r.Close()
default:
r.rw.Lock()
if r.ring.IsEmpty() {
if r.closed { // 如果已关闭并且没有数据,则关闭通道
close(r.ch)
r.rw.Unlock()
return
}
// 等待数据
r.cond.Wait()
}
vs := r.ring.ReadAll()
r.rw.Unlock()
for _, v := range vs {
r.ch <- v
}
}
}
}