feat: 重新设计 delay consumer producer
This commit is contained in:
129
delay/redis_delay.go
Normal file
129
delay/redis_delay.go
Normal file
@@ -0,0 +1,129 @@
|
||||
// Package delay ...
|
||||
//
|
||||
// Description : 基于redis实现的延迟事件队列
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-08-29 09:11
|
||||
package delay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/queue/abstract"
|
||||
"git.zhangdeman.cn/zhangdeman/queue/define"
|
||||
redisPkg "git.zhangdeman.cn/zhangdeman/redis"
|
||||
redisPkgDefine "git.zhangdeman.cn/zhangdeman/redis/define"
|
||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
// NewRedisDelayEvent 获取延迟队列实例
|
||||
// 参数说明:
|
||||
// - redisFlag: 使用那个redis实例
|
||||
// - queueName: 延迟队列名称
|
||||
// - pullTimeInterval: 延迟队列扫描的时间间隔, 单位秒
|
||||
// - distributeProducer: 消息生产者示例, 扫描到的数据通过此实例向二级任务队列分发
|
||||
func NewRedisDelayEvent(redisFlag string, queueName string, pullTimeInterval int64, distributeProducer abstract.IProducer) abstract.IDelayQueue {
|
||||
if queueName == "" || redisFlag == "" {
|
||||
panic("init redis delay event: queue name or redis flag is empty")
|
||||
}
|
||||
if nil == distributeProducer {
|
||||
panic("distributeProducer is nil")
|
||||
}
|
||||
// 验证redis实例是否存在
|
||||
if _, err := redisPkg.Client.GetRealClientWithError(redisFlag); nil != err {
|
||||
panic(err.Error())
|
||||
}
|
||||
if pullTimeInterval <= 0 {
|
||||
pullTimeInterval = 60
|
||||
}
|
||||
return &redisDelayEvent{
|
||||
stopChan: make(chan bool, 1),
|
||||
redisFlag: redisFlag,
|
||||
delayQueueName: queueName,
|
||||
pullTimeInterval: pullTimeInterval,
|
||||
distributeProducer: distributeProducer,
|
||||
}
|
||||
}
|
||||
|
||||
type redisDelayEvent struct {
|
||||
stopChan chan bool // 停止请求的chan
|
||||
redisFlag string // 使用的redis实例
|
||||
delayQueueName string // 延迟队列名称
|
||||
pullTimeInterval int64 // 延迟队列多久拉取一次数据
|
||||
distributeProducer abstract.IProducer // 事件分发的生产者
|
||||
}
|
||||
|
||||
// Send 生成一条延时事件
|
||||
// 参数说明:
|
||||
// - delayTime: 延时时长, 单位: s
|
||||
// - data: 要发送的数据
|
||||
func (rde redisDelayEvent) Send(ctx context.Context, delayTime int64, data *define.EventData) *redisPkgDefine.RedisResult {
|
||||
if delayTime <= 0 {
|
||||
// 默认延迟 1min
|
||||
delayTime = 60
|
||||
}
|
||||
if delayTime >= time.Now().Unix() {
|
||||
// 指定具体的延迟时间, 重置延时时间为相对时间
|
||||
delayTime = delayTime - time.Now().Unix()
|
||||
}
|
||||
return redisPkg.Wrapper.ZAdd(ctx, rde.redisFlag, rde.delayQueueName, time.Now().Unix()+delayTime, serialize.JSON.MarshalForStringIgnoreError(data))
|
||||
}
|
||||
|
||||
// Distribute 到期事件事件分发
|
||||
func (rde redisDelayEvent) Distribute(ctx context.Context) {
|
||||
for {
|
||||
hasFinish := false
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// 收到上下文已完成事件, 退出
|
||||
hasFinish = true
|
||||
case <-rde.stopChan:
|
||||
// 程序调用stop方法, 退出
|
||||
hasFinish = true
|
||||
default:
|
||||
now := time.Now()
|
||||
// 计时器, 固定时长扫描一次
|
||||
// ZRangeAndRemByScore 此方法内部已经自动包装了删除逻辑
|
||||
eventDataListRes := redisPkg.Wrapper.ZRangeAndRemByScore(ctx, rde.redisFlag, rde.delayQueueName, 0, now.Unix())
|
||||
// ZRange 返回数据时二维数组 [["数据部分", "设置的数据score"]]
|
||||
resList := gjson.Parse(eventDataListRes.Result).Array()
|
||||
if len(resList) > 0 {
|
||||
for _, itemData := range resList {
|
||||
var (
|
||||
resFormat define.EventData
|
||||
err error
|
||||
)
|
||||
|
||||
if err = serialize.JSON.UnmarshalWithNumberForString(itemData.Get("0").String(), &resFormat); nil != err {
|
||||
// 数据解析失败, 触发失败回调
|
||||
rde.distributeProducer.GetProducerHandler().FailureCallback(ctx, &define.EventData{
|
||||
Type: "UNMARSHAL_FAILURE",
|
||||
Data: itemData.Get("0").String(),
|
||||
}, &define.EventProduceResult{
|
||||
Queue: "",
|
||||
Success: false,
|
||||
Err: err,
|
||||
Cost: 0,
|
||||
})
|
||||
continue
|
||||
} else {
|
||||
// 生产数据, 向耳机队列分发任务
|
||||
rde.distributeProducer.Sync(ctx, &resFormat)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 间隔指定时间拉取一次
|
||||
time.Sleep(time.Second * time.Duration(rde.pullTimeInterval))
|
||||
}
|
||||
if hasFinish {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rde redisDelayEvent) Stop() {
|
||||
rde.stopChan <- true
|
||||
}
|
||||
Reference in New Issue
Block a user