高性能分布式任务队列Asynq安装和使用实战


前言

一直想找一个比较好用的轻量级的好用的异步任务队列的服务或工具,最好是用golang开发的,不用说太多,太喜欢用 golang开发的一些服务,例如gocron用在多们的生产环境一直很不错,很稳定,有时间了出一个gocron的安装和使用孝程分亨给大家,今天我们还是来看一下如何一步一步的安装Asynq和会使用Asynq


一、Asynq是什么?

Asynq是一个go语言实现的分布式任务队列和异步处理库,基于redis,类似sidekiq和celery,他具有以下特点:

  • 保证至少执行一次任务
  • 持久化
  • 失败重试
  • worker崩溃自动恢复
  • 优先队列
  • 暂停队列
  • 支持中间件
  • 允许唯一任务
  • 支持Redis Cluster实现自动分片
  • 支持Redis Sentinels实现高可用
  • 提供web ui管理
  • 提供cli管理

二、安装步骤

1.安装golang开发和编译环境

Asynq是用go语言开发的,需要golang的开发和编译环境,如果服务器或本地没有golang开发或编译环境,请先看一下我原来的文章:
centos7.9快速安装golang运行和开发环境图文教程

2、安装REDIS服务环境

Asynq使用Redis作为消息代理,Redis Server 版本最好4.0版本以上,具体的安装步骤大家可以参考我原来的文章:
CENTOS7.9源码编译安装REDIS7.0图文教程

3、安装asynqmon服务

虽然Asynq提供了webui 和 命令行工具asynq,用起来很方便,但还是没有asynqmon更直接,最好还是事先安装一下asynqmon,asynqmon是Asynq分布式任务队列实时监视器和Web管理工具
Asynqmon是asynq的一个web UI工具,用于监控和管理Asynq队列和任务。它支持与Prometheus集成来显示时序数据。
Asynqmon既是一个可以包含在web应用程序中的库,也是一个可以简单安装和运行的二进制文件
现在我们看如何安装它
Asynqmon的github的库地址是:
https://github.com/hibiken/asynqmon.git

cd /data/go/src/  #进入go的工作目录
git clone https://github.com/hibiken/asynqmon.git  #拉取项目代码
go mod tidy #拉取asynqmon的项目的go依赖包
make build #打包aqynqmon 

如果打包成功/data/go/src/asynqmon目录会生成一个二进制可执行文件
asynqmon
可能在编译过程中提示需要nodejs的支持和yarn的支持,如果没有node环境请先安装node环境,然后

yarn npm install -g

然后再打包编译asynqmon, 编译成功后我们直接运行:

/data/go/src/asynqmon/asynqmon -port 8809 -redis-addr 172.16.0.38:6379 -redis-db 9

-port 8809 你的asynqmon服务的端口
-redis-addr 你的redis server的服务ip和端口 resdis server ip最好用内网ip
-redis-db 你的asynq服务使用的redis db库编号 0-15

把asynqmon加入supervisor去管理,启动和监控

vi /etc/supervisord.d/asynqmon.ini

增加内容:

[program:asynqmon]
directory=/data/go/src/asynqmon/
command=/data/go/src/asynqmon/asynqmon -port 8809 -redis-addr 172.16.0.38:6379 -redis-db 9
numprocs=1
redirect_stderr=true
autostart=true
autorestart=true
user=root
stdout_logfile=/data/logs/go/asynqmon.log

:wq保存后,启supervisor控制台,update 一下后启运asynqmon服务
在这里插入图片描述
最后在用浏览器打开asynqmon可以看到效果:
在这里插入图片描述

4、新建go项目,实现asynq的produce和consume服务

make /data/go/src/asynq
make /data/go/src/asynq/producer
make /data/go/src/asynq/consumer
make /data/go/src/asynq/consumer/task
go mod init asynq
vim /data/go/src/asynq/producer/producer.go
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-redis/redis/v8"
	"github.com/hibiken/asynq"
	"log"
	"time"
)
type Tqueue struct {
	// typename indicates the type of task to be performed.
	T string `json:"t"`

	// payload holds data needed to perform the task.
	P string `json:"p"`
    // add to  task times
	C int `json:"c"`
}
var ctx context.Context
var rdb *redis.Client
var asynqClient *asynq.Client
func main() {
	log.Printf("start producer main")
	ctx = context.Background()
	rdb = redis.NewClient(&redis.Options{
		Addr:     "172.16.0.38:6379",
		Password: "", // no password set
		DB:       8,  // use default DB
	})
	ra := asynq.RedisClientOpt{Addr: "172.16.0.38:6379", DB:9}
	asynqClient = asynq.NewClient(ra)

	defer func() {
		rdb.Close()
		asynqClient.Close()
	}()
	for {
		result, err := rdb.BLPop(ctx,10*time.Second,"wdmqueen").Result()

		if err != nil {
			log.Printf(" ... ")
			continue
		}
		log.Printf("get result 0 : %s", result[0])
		log.Printf("get result 1 : %s ", result[1])
		go AddNewTask(result[1])
	}
}
func AddNewTask(data string) error {
	log.Printf("start add new task")
	var t Tqueue
	if err := json.Unmarshal([]byte(data), &t); err != nil {
		log.Printf("Unmarshal err %s,%v",data,err)
		return fmt.Errorf("json.Unmarshal failed: %s: %v", data,err)
	}
	t1 := asynq.NewTask(t.T, []byte(t.P))
	info, err := asynqClient.Enqueue(t1)
	if err != nil {
		log.Fatal(err)
		if t.C <10 {
			t.C +=1
			str, _ := json.Marshal(t)
			rdb.RPush(ctx,"wdmqueen", string(str))
		}
	}
	log.Printf("enqueued task: id=%s queue=%s", info.ID, info.Queue)
	return nil
}
vi /data/go/src/asynq/consumer/consumer.go
package main

import (
	"github.com/hibiken/asynq"
	"log"
	"wdmasynq/consumer/task"
)

func main() {
	rd := asynq.RedisClientOpt{Addr: "172.16.0.38:6379", DB:9}
	srv := asynq.NewServer(rd, asynq.Config{
		Concurrency: 10,
	})

	r := asynq.NewServeMux()
	r .HandleFunc("email:welcome", task.SendWelcomeEmail)
	r .HandleFunc("email:reminder", task.SendReminderEmail)

	if err := srv.Run(r); err != nil {
		log.Fatal(err)
	}
}
vi /data/go/src/asynq/consumer/task/sendmail.go
package task

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/hibiken/asynq"
	"log"
)
type EmailDeliveryPayload struct {
	UserID     int
	TemplateID string
}
func SendWelcomeEmail(ctx context.Context, t *asynq.Task) error {
	var p EmailDeliveryPayload
	if err := json.Unmarshal(t.Payload(), &p); err != nil {
		return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
	}
	log.Printf("Sending Email to User: user_id=%d, template_id=%s", p.UserID, p.TemplateID)
	// Email delivery code ...
	return nil
}
func SendReminderEmail(ctx context.Context, t*asynq.Task) error {
	var p EmailDeliveryPayload
	if err := json.Unmarshal(t.Payload(), &p); err != nil {
		return fmt.Errorf("json.Unmarshal failed: %v: %w", err, asynq.SkipRetry)
	}
	log.Printf("Sending Reminder Email to User: user_id=%d, template_id=%s", p.UserID, p.TemplateID)
	return nil
}

然后拉取一下用到的依赖包

go mod tidy

然后启动两个终端分别运行:

go run consumer/consumer.go
go run producer/producer.go

就可以看到效果了,我这里的producer是从redis的另一个db取的,已用于实际的生产环境,由于实际的业务是用php写的,php想把任务加入asynq的队列中,中间中转了一下,整体的服务架构图:

在这里插入图片描述

最后把我在asynq的讨论区的问题截个图,供大家参考一下:
在这里插入图片描述
提供了一种php使用asynq的方法


总结

asynq轻量式高性能分布式任务队列库,开发自谷歌员工,必是精品。

如有问题,欢迎大家留言沟通,点赞支持!!

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北漂燕郊杨哥

您的支持是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值