fix: 修复 dispatcher.Dispatcher 在消息归零的时候使用协程运行处理函数可能导致不可知问题的情况,修复消息消费时获取生产者可能已经被释放的问题。修复在无消息时候设置消息完成处理函数不会触发一次的问题

This commit is contained in:
kercylan98 2024-01-12 15:29:42 +08:00
parent a2a9199d41
commit 7528dc4a1b
4 changed files with 98 additions and 23 deletions

View File

@ -0,0 +1,30 @@
package dispatcher
// Action 消息分发器操作器,用于暴露外部可操作的消息分发器函数
type Action[P Producer, M Message[P]] struct {
unlock bool
d *Dispatcher[P, M]
}
// Name 获取消息分发器名称
func (a *Action[P, M]) Name() string {
return a.d.Name()
}
// UnExpel 取消特定生产者的驱逐计划
func (a *Action[P, M]) UnExpel() {
if !a.unlock {
a.d.UnExpel()
} else {
a.d.noLockUnExpel()
}
}
// Expel 设置该消息分发器即将被驱逐,当消息分发器中没有任何消息时,会自动关闭
func (a *Action[P, M]) Expel() {
if !a.unlock {
a.d.Expel()
} else {
a.d.noLockExpel()
}
}

View File

@ -4,6 +4,8 @@ import (
"fmt"
"github.com/alphadose/haxmap"
"github.com/kercylan98/minotaur/utils/buffer"
"github.com/kercylan98/minotaur/utils/log"
"github.com/kercylan98/minotaur/utils/super"
"sync"
"sync/atomic"
)
@ -24,7 +26,7 @@ func NewDispatcher[P Producer, M Message[P]](bufferSize int, name string, handle
handler: handler,
uniques: haxmap.New[string, struct{}](),
pmc: make(map[P]int64),
pmcF: make(map[P]func(p P, dispatcher *Dispatcher[P, M])),
pmcF: make(map[P]func(p P, dispatcher *Action[P, M])),
abort: make(chan struct{}),
}
return d
@ -50,28 +52,40 @@ type Dispatcher[P Producer, M Message[P]] struct {
expel bool
mc int64
pmc map[P]int64
pmcF map[P]func(p P, dispatcher *Dispatcher[P, M])
pmcF map[P]func(p P, dispatcher *Action[P, M])
lock sync.RWMutex
name string
closedHandler atomic.Pointer[func(dispatcher *Dispatcher[P, M])]
closedHandler atomic.Pointer[func(dispatcher *Action[P, M])]
abort chan struct{}
}
// SetProducerDoneHandler 设置特定生产者所有消息处理完成时的回调函数
// - 如果 handler 为 nil则会删除该生产者的回调函数
func (d *Dispatcher[P, M]) SetProducerDoneHandler(p P, handler func(p P, dispatcher *Dispatcher[P, M])) *Dispatcher[P, M] {
//
// 需要注意的是,该 handler 中
func (d *Dispatcher[P, M]) SetProducerDoneHandler(p P, handler func(p P, dispatcher *Action[P, M])) *Dispatcher[P, M] {
d.lock.Lock()
if handler == nil {
delete(d.pmcF, p)
} else {
d.pmcF[p] = handler
if pmc := d.pmc[p]; pmc <= 0 {
func(producer P, handler func(p P, dispatcher *Action[P, M])) {
defer func(producer P) {
if err := super.RecoverTransform(recover()); err != nil {
log.Error("Dispatcher.ProducerDoneHandler", log.Any("producer", producer), log.Err(err))
}
}(p)
handler(p, &Action[P, M]{d: d, unlock: true})
}(p, handler)
}
}
d.lock.Unlock()
return d
}
// SetClosedHandler 设置消息分发器关闭时的回调函数
func (d *Dispatcher[P, M]) SetClosedHandler(handler func(dispatcher *Dispatcher[P, M])) *Dispatcher[P, M] {
func (d *Dispatcher[P, M]) SetClosedHandler(handler func(dispatcher *Action[P, M])) *Dispatcher[P, M] {
d.closedHandler.Store(&handler)
return d
}
@ -95,29 +109,38 @@ func (d *Dispatcher[P, M]) AntiUnique(name string) {
// Expel 设置该消息分发器即将被驱逐,当消息分发器中没有任何消息时,会自动关闭
func (d *Dispatcher[P, M]) Expel() {
d.lock.Lock()
d.noLockExpel()
d.lock.Unlock()
}
func (d *Dispatcher[P, M]) noLockExpel() {
d.expel = true
if d.mc <= 0 {
d.abort <- struct{}{}
}
d.lock.Unlock()
}
// UnExpel 取消特定生产者的驱逐计划
func (d *Dispatcher[P, M]) UnExpel() {
d.lock.Lock()
d.expel = false
d.noLockUnExpel()
d.lock.Unlock()
}
func (d *Dispatcher[P, M]) noLockUnExpel() {
d.expel = false
}
// IncrCount 主动增量设置特定生产者的消息计数,这在等待异步消息完成后再关闭消息分发器时非常有用
// - 如果 i 为负数,则会减少消息计数
func (d *Dispatcher[P, M]) IncrCount(producer P, i int64) {
d.lock.Lock()
defer d.lock.Unlock()
d.mc += i
d.pmc[producer] += i
if d.expel && d.mc <= 0 {
d.abort <- struct{}{}
}
d.lock.Unlock()
}
// Put 将消息放入分发器
@ -129,7 +152,7 @@ func (d *Dispatcher[P, M]) Put(message M) {
d.buf.Write(message)
}
// Start 以阻塞的方式开始进行消息分发,当消息分发器中没有任何消息时,会自动关闭
// Start 以阻塞的方式开始进行消息分发,当消息分发器中没有任何消息并且处于驱逐计划 Expel 时,会自动关闭
func (d *Dispatcher[P, M]) Start() *Dispatcher[P, M] {
go func(d *Dispatcher[P, M]) {
process:
@ -139,25 +162,33 @@ func (d *Dispatcher[P, M]) Start() *Dispatcher[P, M] {
d.buf.Close()
break process
case message := <-d.buf.Read():
// 先取出生产者信息,避免处理函数中将消息释放
p := message.GetProducer()
d.handler(d, message)
d.lock.Lock()
d.mc--
p := message.GetProducer()
pmc := d.pmc[p] - 1
d.pmc[p] = pmc
if f := d.pmcF[p]; f != nil && pmc <= 0 {
go f(p, d)
func(producer P) {
defer func(producer P) {
if err := super.RecoverTransform(recover()); err != nil {
log.Error("Dispatcher.ProducerDoneHandler", log.Any("producer", producer), log.Err(err))
}
}(p)
f(p, &Action[P, M]{d: d, unlock: true})
}(p)
}
if d.mc <= 0 && d.expel {
d.buf.Close()
d.lock.Unlock()
break process
}
d.lock.Unlock()
}
}
closedHandler := *(d.closedHandler.Load())
if closedHandler != nil {
closedHandler(d)
if ch := d.closedHandler.Load(); ch != nil {
(*ch)(&Action[P, M]{d: d, unlock: true})
}
close(d.abort)
}(d)

View File

@ -12,10 +12,13 @@ func NewManager[P Producer, M Message[P]](bufferSize int, handler Handler[P, M])
handler: handler,
dispatchers: make(map[string]*Dispatcher[P, M]),
member: make(map[string]map[P]struct{}),
sys: NewDispatcher(bufferSize, SystemName, handler).Start(),
sys: NewDispatcher(bufferSize, SystemName, handler),
curr: make(map[P]*Dispatcher[P, M]),
size: bufferSize,
}
mgr.sys.SetClosedHandler(func(dispatcher *Action[P, M]) {
mgr.w.Done()
}).Start()
return mgr
}
@ -35,6 +38,14 @@ type Manager[P Producer, M Message[P]] struct {
createdHandler func(name string)
}
// Wait 等待所有消息分发器关闭
func (m *Manager[P, M]) Wait() {
m.w.Wait()
m.w.Add(1)
m.sys.Expel()
m.w.Wait()
}
// SetDispatcherClosedHandler 设置消息分发器关闭时的回调函数
func (m *Manager[P, M]) SetDispatcherClosedHandler(handler func(name string)) *Manager[P, M] {
m.closedHandler = handler
@ -111,15 +122,17 @@ func (m *Manager[P, M]) BindProducer(p P, name string) {
dispatcher, exist := m.dispatchers[name]
if !exist {
dispatcher = NewDispatcher(m.size, name, m.handler).SetClosedHandler(func(dispatcher *Dispatcher[P, M]) {
m.w.Add(1)
dispatcher = NewDispatcher(m.size, name, m.handler).SetClosedHandler(func(dispatcher *Action[P, M]) {
// 消息分发器关闭时,将会将其从管理器中移除
m.lock.Lock()
delete(m.dispatchers, dispatcher.name)
delete(m.member, dispatcher.name)
delete(m.dispatchers, dispatcher.Name())
delete(m.member, dispatcher.Name())
m.lock.Unlock()
if m.closedHandler != nil {
m.closedHandler(dispatcher.name)
m.closedHandler(dispatcher.Name())
}
m.w.Done()
}).Start()
m.dispatchers[name] = dispatcher
defer func(m *Manager[P, M], name string) {
@ -135,18 +148,18 @@ func (m *Manager[P, M]) BindProducer(p P, name string) {
// UnBindProducer 解绑生产者使用特定的消息分发器
func (m *Manager[P, M]) UnBindProducer(p P) {
m.lock.Lock()
defer m.lock.Unlock()
curr, exist := m.curr[p]
m.lock.Unlock()
if !exist {
return
}
curr.SetProducerDoneHandler(p, func(p P, dispatcher *Dispatcher[P, M]) {
curr.SetProducerDoneHandler(p, func(p P, dispatcher *Action[P, M]) {
m.lock.Lock()
defer m.lock.Unlock()
delete(m.member[dispatcher.name], p)
delete(m.member[dispatcher.Name()], p)
delete(m.curr, p)
if len(m.member[dispatcher.name]) == 0 {
if len(m.member[dispatcher.Name()]) == 0 {
dispatcher.Expel()
}
})

View File

@ -102,6 +102,7 @@ func (slf *Message) reset() {
slf.name = ""
slf.t = 0
slf.marks = nil
slf.producer = ""
}
// MessageType 返回消息类型