feat: 重新设计 delay consumer producer
This commit is contained in:
@@ -1,78 +1,116 @@
|
||||
// Package delay ...
|
||||
// Package event ...
|
||||
//
|
||||
// Description : delay ...
|
||||
// Description : 基于redis实现事件生产 + 消费
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2022-07-06 17:59
|
||||
// Date : 2025-08-22 16:34
|
||||
package delay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/network/util"
|
||||
"git.zhangdeman.cn/zhangdeman/queue/abstract"
|
||||
"git.zhangdeman.cn/zhangdeman/queue/define"
|
||||
"git.zhangdeman.cn/zhangdeman/redis"
|
||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||
"git.zhangdeman.cn/zhangdeman/wrapper/op_string"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// NewRedisQueue 获取redis队列实例
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:09 2022/7/6
|
||||
func NewRedisQueue(redisInstance *redis.Client) IProduce {
|
||||
return &redisProduce{client: redisInstance}
|
||||
}
|
||||
|
||||
// withRedis 使用redis实现延迟队列
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:59 2022/7/6
|
||||
type redisProduce struct {
|
||||
client *redis.Client
|
||||
}
|
||||
|
||||
// Produce 生产数据
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:03 2022/7/6
|
||||
func (rp *redisProduce) Produce(ctx context.Context, data ...*Queue) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
// NewRedisProducer 获取基于redis的生产者实例
|
||||
func NewRedisProducer(
|
||||
queueName string, queueCnt int, redisFlag string,
|
||||
producerHandler abstract.IProducerHandler,
|
||||
) abstract.IProducer {
|
||||
if queueName == "" || redisFlag == "" {
|
||||
panic("init redis producer: queue name or redis flag is empty")
|
||||
}
|
||||
if nil == ctx {
|
||||
ctx = context.Background()
|
||||
// 验证redis实例是否存在
|
||||
if _, err := redis.Client.GetRealClientWithError(redisFlag); nil != err {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(len(data))
|
||||
for _, queueData := range data {
|
||||
go func(inputQueueData *Queue) {
|
||||
defer wg.Done()
|
||||
inputQueueData.err = rp.client.ZAdd(ctx, inputQueueData.Name, rp.buildAddMember(inputQueueData)).Err()
|
||||
}(queueData)
|
||||
if queueCnt <= 0 {
|
||||
queueCnt = 1 // 默认单队列
|
||||
}
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildAddMember ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:22 2022/7/6
|
||||
func (rp *redisProduce) buildAddMember(queueData *Queue) redis.Z {
|
||||
return redis.Z{
|
||||
Score: float64(time.Now().Unix() + queueData.DelayTime),
|
||||
Member: serialize.JSON.MarshalForStringIgnoreError(&ProduceData{
|
||||
MsgID: op_string.RandomMd5().Value,
|
||||
Timestamp: time.Now().UnixNano() / 1e6,
|
||||
Host: "",
|
||||
Data: queueData.Data,
|
||||
}),
|
||||
if nil == producerHandler {
|
||||
producerHandler = abstract.DefaultProducerHandler{}
|
||||
}
|
||||
return &redisProducer{
|
||||
queueCnt: queueCnt,
|
||||
queueName: queueName,
|
||||
redisFlag: redisFlag,
|
||||
producerHandler: producerHandler,
|
||||
}
|
||||
}
|
||||
|
||||
type redisProducer struct {
|
||||
redisFlag string // redis 标识, 必须是使用统一的 pkg/redis 进行管理的
|
||||
queueName string // 队列名称,基础公共前缀,尾部后缀会自动根据 shard cnt 进行哈希
|
||||
queueCnt int // 队列数量
|
||||
producerHandler abstract.IProducerHandler // 生产数据的处理逻辑
|
||||
}
|
||||
|
||||
// fillEventData 填充一些系统数据数据
|
||||
func (r *redisProducer) fillEventData(data *define.EventData) {
|
||||
if len(data.Key) == 0 {
|
||||
data.Key = data.MsgID // 不设置和MsgID一致
|
||||
}
|
||||
if len(data.TraceID) == 0 {
|
||||
data.TraceID = data.Key
|
||||
}
|
||||
data.SystemTimestamp = time.Now().UnixMilli() // 系统时间
|
||||
if data.Timestamp <= 0 {
|
||||
data.Timestamp = data.SystemTimestamp
|
||||
}
|
||||
data.Hostname, _ = os.Hostname() // 服务器 hostname
|
||||
data.HostIp = util.IP.GetHostIP() // 服务器IP
|
||||
}
|
||||
|
||||
// Sync 同步发送
|
||||
func (r *redisProducer) Sync(ctx context.Context, data *define.EventData) {
|
||||
if nil == data {
|
||||
return
|
||||
}
|
||||
r.fillEventData(data)
|
||||
realQueue := r.getRealQueue(data)
|
||||
sendRedisRes := redis.Wrapper.LPush(ctx, r.redisFlag, realQueue, serialize.JSON.MarshalForStringIgnoreError(data))
|
||||
res := &define.EventProduceResult{
|
||||
Queue: realQueue,
|
||||
Success: sendRedisRes.Err == nil,
|
||||
Err: sendRedisRes.Err,
|
||||
Cost: sendRedisRes.UsedTime,
|
||||
}
|
||||
if res.Success {
|
||||
r.producerHandler.SuccessCallback(ctx, data, res)
|
||||
} else {
|
||||
r.producerHandler.FailureCallback(ctx, data, res)
|
||||
}
|
||||
}
|
||||
|
||||
// Async 异步发送
|
||||
func (r *redisProducer) Async(ctx context.Context, data *define.EventData) {
|
||||
go func() {
|
||||
defer func() {
|
||||
// 如果出现panic, 则触发失败回调
|
||||
if err := recover(); nil != err {
|
||||
r.producerHandler.PanicCallback(ctx, data, err)
|
||||
}
|
||||
}()
|
||||
r.Sync(ctx, data)
|
||||
}()
|
||||
}
|
||||
|
||||
// GetProducerHandler 获取producer处理器
|
||||
func (r *redisProducer) GetProducerHandler() abstract.IProducerHandler {
|
||||
return r.producerHandler
|
||||
}
|
||||
|
||||
// getRealQueue 获取真实的队列key
|
||||
func (r *redisProducer) getRealQueue(data *define.EventData) string {
|
||||
queueIdx := op_string.HashNumber(data.Key).Value % uint64(r.queueCnt)
|
||||
return fmt.Sprintf("%v_%v", r.queueName, queueIdx)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user