feat: 新版 server 包 websocket 基础实现、actor 模型实现
This commit is contained in:
33
utils/super/counter.go
Normal file
33
utils/super/counter.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
8
utils/super/read_writer.go
Normal file
8
utils/super/read_writer.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package super
|
||||
|
||||
import "io"
|
||||
|
||||
type ReadWriter struct {
|
||||
io.Reader
|
||||
io.Writer
|
||||
}
|
||||
78
utils/unbounded/channel_backlog.go
Normal file
78
utils/unbounded/channel_backlog.go
Normal 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
94
utils/unbounded/ring.go
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user