54 lines
1.5 KiB
Go
54 lines
1.5 KiB
Go
package integration
|
||
|
||
import (
|
||
"context"
|
||
"hash/fnv"
|
||
"sync"
|
||
|
||
roomeventsv1 "hyapp.local/api/proto/events/room/v1"
|
||
)
|
||
|
||
// ShardedOutboxPublisher 只在 IM bridge 侧使用:不同 room_id 可以并发,同一个房间仍串行调用下游发布器。
|
||
type ShardedOutboxPublisher struct {
|
||
inner OutboxPublisher
|
||
locks []sync.Mutex
|
||
}
|
||
|
||
// NewShardedOutboxPublisher 用 room_id 分片保护下游发布器的房间内顺序;concurrency<=1 时保持历史串行实现。
|
||
func NewShardedOutboxPublisher(inner OutboxPublisher, concurrency int) OutboxPublisher {
|
||
if inner == nil {
|
||
return NewNoopOutboxPublisher()
|
||
}
|
||
if concurrency <= 1 {
|
||
return inner
|
||
}
|
||
return &ShardedOutboxPublisher{
|
||
inner: inner,
|
||
locks: make([]sync.Mutex, concurrency),
|
||
}
|
||
}
|
||
|
||
// PublishOutboxEvent 对同一 room_id 使用同一把锁,避免 RocketMQ consumer 并发后把礼物和麦位展示乱序发给腾讯 IM。
|
||
func (p *ShardedOutboxPublisher) PublishOutboxEvent(ctx context.Context, envelope *roomeventsv1.EventEnvelope) error {
|
||
if p == nil || p.inner == nil || envelope == nil {
|
||
return nil
|
||
}
|
||
if len(p.locks) == 0 {
|
||
return p.inner.PublishOutboxEvent(ctx, envelope)
|
||
}
|
||
lock := &p.locks[p.shardIndex(envelope.GetRoomId())]
|
||
lock.Lock()
|
||
defer lock.Unlock()
|
||
|
||
return p.inner.PublishOutboxEvent(ctx, envelope)
|
||
}
|
||
|
||
func (p *ShardedOutboxPublisher) shardIndex(roomID string) uint32 {
|
||
if len(p.locks) == 1 {
|
||
return 0
|
||
}
|
||
hash := fnv.New32a()
|
||
_, _ = hash.Write([]byte(roomID))
|
||
return hash.Sum32() % uint32(len(p.locks))
|
||
}
|