go语言使用redis实现异步任务

目前go语言有一些比较可靠的异步队列的开源组件比如asynq、Machinery等,但是组件一个有学习成本,二是出现问题的时候比较头疼,排查起来比较费时间还要分析源码,所以自己比较倾向于写一个轻量级。

此过程使用的是redis的list,左进右出,一般生产者使用lpush压入数据,消费者调用rpop取出数据。这个具体看自己的需求,如果任务是可以做到幂等操作的,可以使用lrange+ltrim替代lpop,在处理成功后调用ltrim,这样可做到至少处理一次。

首先在应用启动的时候初始话加载redis连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package initialization
 
import (
    "context"
    "fmt"
    "log"
    "time"
 
    "github.com/go-redis/redis/v8"
)
 
var RedisClient *redis.Client
 
type RedisOptoins struct {
    Host     string `yaml:"host" json:"host"`
    Port     int    `yaml:"port" json:"port"`
    DB       int    `yaml:"db" json:"db"`
    PoolSize int    `yaml:"pool_size" json:"pool_size"`
    MinIdle  int    `yaml:"min_idle" json:"min_idle"`
}
 
func NewRedisClient() *redis.Client {
    redis_config := Config.Redis
    RedisClient = redis.NewClient(&redis.Options{
        Addr:         fmt.Sprintf("%s:%d", redis_config.Host, redis_config.Port),
        PoolSize:     redis_config.PoolSize,
        MinIdleConns: redis_config.MinIdle,
        DB:           redis_config.DB,
        PoolTimeout:  time.Second * 5,
        WriteTimeout: time.Second * 5,
 
        IdleCheckFrequency: 60 * time.Second,  //闲置连接检查的周期,默认为1分钟,-1表示不做周期性检查,只在客户端获取连接时对闲置连接进行处理。
        IdleTimeout:        5 * time.Minute,   //闲置超时,默认5分钟,-1表示取消闲置超时检查
        MaxConnAge:         600 * time.Second, //连接存活时长,从创建开始计时,超过指定时长则关闭连接,默认为0,即不关闭存活时长较长的连接
 
        //命令执行失败时的重试策略
        MaxRetries:      1,                      // 命令执行失败时,最多重试多少次,默认为0即不重试
        MinRetryBackoff: 8 * time.Millisecond,   //每次计算重试间隔时间的下限,默认8毫秒,-1表示取消间隔
        MaxRetryBackoff: 512 * time.Millisecond, //每次计算重试间隔时间的上限,默认512毫秒,-1表示取消间隔
    })
    var ctx = context.Background()
    pong, err := RedisClient.Ping(ctx).Result()
    if err != nil {
        fmt.Println("redis 连接失败:", pong, err)
        log.Panicf("redis connect failed: %v", err)
    }
    return RedisClient
}

  

然后将任务写入redis

1
2
3
if err := initialization.RedisClient.LPush(ctx, string(QueueName), string(queueValue)).Err(); err != nil {
    return err
}

  

然后启动一个异步监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func ListenQueue(queueName string) {
    // 确保即使某个代码执行异常,整个程序不会panic中断
    defer func() {
        err := recover()
        if err != nil {
            log.Println("程序遭遇严重异常!!!!")
            ListenQueue(queueName)
        }
    }()
    for {
        ctx := initialization.NewTraceIDContext()                                               // 每个业务启动创建一个trace_id
        values, err := initialization.RedisClient.BRPop(ctx, 5*time.Second, queueName).Result() // 设置一个5秒的超时时间
        if err == redis.Nil {                                                                   // 查询不到数据
            time.Sleep(1 * time.Second)
            continue
        }
        if err != nil { // 查询出错,则每隔10秒重新查询
            time.Sleep(10 * time.Second)
            continue
        }
 
        initialization.WithContext(ctx).Info("消费到数据:", values)
        if len(values) != 2 {
            initialization.WithContext(ctx).Errorf("数据格式不正确,需要长度为2,但实际长度%d", len(values))
            continue
        }
 
        // TODO 消费操作,values是数组,但理论上values的默认长度是1
        QueueExecute(ctx, queueName, values[1]) // 实际执行任务处理的方法
    }
}

  

 用这种方式,需要注意,超时时间要稍微长一些,因为超时时间短了,会触发重试,一旦重试可能会造成队列里面的数据丢失

posted @   一个和🔥有缘的人  阅读(423)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2017-06-26 xxx.app已损坏,打不开.你应该将它移到废纸篓-已解决
点击右上角即可分享
微信分享提示