golang之异步队列Asynq
Asynq[1]是一个Go实现的分布式任务队列和异步处理库,基于redis,类似Ruby的sidekiq[2]和Python的celery[3]。Go生态类似的还有machinery[4]和goworker
同时提供一个WebUI asynqmon[5],可以源码形式安装或使用Docker image, 还可以和Prometheus集成
docker run --rm --name asynqmon -p 8080:8080 hibiken/asynqmon,如果使用的是主机上的redis,还需加上 --redis-addr=host.docker.internal:6379,否则会报错[6]
即 docker run --rm --name asynqmon -p 8080:8080 hibiken/asynqmon --redis-addr=host.docker.internal:6379
➜  asynq-demo git:(main) ✗ tree
.
├── client.go
├── const.go
├── go.mod
├── go.sum
└── server.go
0 directories, 5 files
其中const.go:
package main
const (
 redisAddr   = "127.0.0.1:6379"
 redisPasswd = ""
)
const (
 TypeExampleTask    = "shuang:asynq-task:example"
)
client.go:
package main
import (
 "encoding/json"
 "fmt"
 "log"
 "time"
 "github.com/hibiken/asynq"
)
type ExampleTaskPayload struct {
 UserID string
 Msg    string
 // 业务需要的其他字段
}
func NewExampleTask(userID string, msg string) (*asynq.Task, error) {
 payload, err := json.Marshal(ExampleTaskPayload{UserID: userID, Msg: msg})
 if err != nil {
  return nil, err
 }
 return asynq.NewTask(TypeExampleTask, payload), nil
}
var client *asynq.Client
func main() {
 client = asynq.NewClient(asynq.RedisClientOpt{Addr: redisAddr, Password: redisPasswd, DB: 0})
 defer client.Close()
 //go startExampleTask()
 startExampleTask()
 //startGithubUpdate() // 定时触发
}
func startExampleTask() {
 fmt.Println("开始执行一次性的任务")
 // 立刻执行
 task1, err := NewExampleTask("10001", "mashangzhixing!")
 if err != nil {
  log.Fatalf("could not create task: %v", err)
 }
 info, err := client.Enqueue(task1)
 if err != nil {
  log.Fatalf("could not enqueue task: %v", err)
 }
 log.Printf("task1 -> enqueued task: id=%s queue=%s", info.ID, info.Queue)
 // 10秒后执行(定时执行)
 task2, err := NewExampleTask("10002", "10s houzhixing")
 if err != nil {
  log.Fatalf("could not create task: %v", err)
 }
 info, err = client.Enqueue(task2, asynq.ProcessIn(10*time.Second))
 if err != nil {
  log.Fatalf("could not enqueue task: %v", err)
 }
 log.Printf("task2 -> enqueued task: id=%s queue=%s", info.ID, info.Queue)
 // 30s后执行(定时执行)
 task3, err := NewExampleTask("10003", "30s houzhixing")
 if err != nil {
  log.Fatalf("could not create task: %v", err)
 }
 theTime := time.Now().Add(30 * time.Second)
 info, err = client.Enqueue(task3, asynq.ProcessAt(theTime))
 if err != nil {
  log.Fatalf("could not enqueue task: %v", err)
 }
 log.Printf("task3 -> enqueued task: id=%s queue=%s", info.ID, info.Queue)
}
server.go:
package main
import (
 "context"
 "encoding/json"
 "fmt"
 "time"
 "github.com/davecgh/go-spew/spew"
 "github.com/hibiken/asynq"
)
var AsynqServer *asynq.Server // 异步任务server
func initTaskServer() error {
 // 初始化异步任务服务端
 AsynqServer = asynq.NewServer(
  asynq.RedisClientOpt{
   Addr:     redisAddr,
   Password: redisPasswd, //与client对应
   DB:       0,
  },
  asynq.Config{
   // Specify how many concurrent workers to use
   Concurrency: 100,
   // Optionally specify multiple queues with different priority.
   Queues: map[string]int{
    "critical": 6,
    "default":  3,
    "low":      1,
   },
   // See the godoc for other configuration options
  },
 )
 return nil
}
func main() {
 initTaskServer()
 mux := asynq.NewServeMux()
 mux.HandleFunc(TypeExampleTask, HandleExampleTask)
 // ...register other handlers...
 if err := AsynqServer.Run(mux); err != nil {
  fmt.Printf("could not run asynq server: %v", err)
 }
}
func HandleExampleTask(ctx context.Context, t *asynq.Task) error {
 res := make(map[string]string)
 spew.Dump("t.Payload() is:", t.Payload())
 err := json.Unmarshal(t.Payload(), &res)
 if err != nil {
  fmt.Printf("rum session, can not parse payload: %s,  err: %v", t.Payload(), err)
  return nil
 }
 //-----------具体处理逻辑------------
 spew.Println("拿到的入参为:", res, "接下来将进行具体处理")
 fmt.Println()
 // 模拟具体的处理
 time.Sleep(5 * time.Second)
 fmt.Println("--------------处理了5s,处理完成-----------------")
 return nil
}
执行redis-server
清除redis中所有的key:
执行docker run --rm --name asynqmon -p 8080:8080 hibiken/asynqmon --redis-addr=host.docker.internal:6379
执行 go run client.go const.go (生产者,产生消息放入队列)
此时能看到redis中多个几个key
同时管理后台能看到队列的信息
执行 go run server.go const.go (消费者,消费队列中的消息)
可以看到都被处理了
此时redis中的key:
此处的业务处理为模拟,实际可能是某个被触发后不需要马上执行的操作
相关文档:
golang之异步队列Asynq的更多相关文章
- .Net中的并行编程-4.实现高性能异步队列
		
上文<.Net中的并行编程-3.ConcurrentQueue实现与分析>分析了ConcurrentQueue的实现,本章就基于ConcurrentQueue实现一个高性能的异步队列,该队 ...
 - jquery ajax 对异步队列defer与XMLHttprequest.onload的依赖
		
ajax 对异步队列defer与XMLHttprequest.onload的依赖
 - 异步队列 Deferred
		
异步队列 Deferred 背景: 移动web app开发,异步代码是时常的事,比如有常见的异步操作: Ajax(XMLHttpRequest) Image Tag,Script Tag,iframe ...
 - [js高手之路]javascript腾讯面试题学习封装一个简易的异步队列
		
这道js的面试题,是这样的,页面上有一个按钮,一个ul,点击按钮的时候,每隔1秒钟向ul的后面追加一个li, 一共追加10个,li的内容从0开始技术( 0, 1, 2, ....9 ),首先我们用闭包 ...
 - .Net中的并行编程-7.基于BlockingCollection实现高性能异步队列
		
三年前写过基于ConcurrentQueue的异步队列,今天在整理代码的时候发现当时另外一种实现方式-使用BlockingCollection实现,这种方式目前依然在实际项目中使用.关于Blockin ...
 - 基于异步队列的生产者消费者C#并发设计
		
继上文<<基于阻塞队列的生产者消费者C#并发设计>>的并发队列版本的并发设计,原文code是基于<<.Net中的并行编程-4.实现高性能异步队列>>修改 ...
 - js异步队列之理解
		
起因 最近看到一篇关于js异步执行顺序的解答,觉得有所收获,遂记录下来. marcotask和microtask js中异步队列可以分为两类,marcotask队列和microtask队列, marc ...
 - jQuery源码分析(九) 异步队列模块 Deferred 详解
		
deferred对象就是jQuery的回调函数解决方案,它解决了如何处理耗时操作的问题,比如一些Ajax操作,动画操作等.(P.s:紧跟上一节:https://www.cnblogs.com/grea ...
 - jQuery 源码解析(八) 异步队列模块 Callbacks 回调函数详解
		
异步队列用于实现异步任务和回调函数的解耦,为ajax模块.队列模块.ready事件提供基础功能,包含三个部分:Query.Callbacks(flags).jQuery.Deferred(funct) ...
 - Laravel异步队列全攻略
		
最近项目需求,研究了laravel的异步队列.官方文档虽然很是详细,但也有些晦涩难懂,在此记录下步骤,供大家参考. 1.修改/config/queue.php文件 <?php return [ ...
 
随机推荐
- SpringMVC:SpringMVC执行流程
			
目录 SpringMVC常用组件 DispatcherServlet初始化过程 ①初始化WebApplicationContext ②创建WebApplicationContext ③Dispatch ...
 - sql server 将数据库表里面的数据,转为insert语句,方便小批量转移数据
			
create proc [dbo].[proc_insert] (@tablename varchar(256)) as begin set nocount on declare @sqlstr va ...
 - JDBC,SQL注入,事务,C3P0于Druid连接池(最详细解析)
			
JDBC JDBC(Java DataBase Connectivty,Java数据库连接)API,是一种用于执行Sql语句的Java API,可以为关系型数据库提供统一的访问,其由一组Java编写的 ...
 - C# and TypeScript – Enum Flags
			
前言 以前就有提过 Enum Flags,但平日不常用.最近翻 Angular 源码,发现它很多地方用到,而且没有封装语义代码.一堆符号真的看不惯啊... 于是又去复习了一遍,熟悉熟悉.顺便写一篇做记 ...
 - Angular 18+ 高级教程 – Component 组件 の Pipe 管道
			
介绍 Pipe 类似于 Template Syntax,它的用途是 transform value for display. 参考: Docs – Understanding Pipes DatePi ...
 - 记一次 公司.NET项目部署在Linux环境压测时 内存暴涨分析
			
一:背景 讲故事 公司部署在某碟上的项目在9月份压测50并发时,发现某个容器线程.内存非正常的上涨,导致功能出现了异常无法使用.根据所学,自己分析了下线程和内存问题,分析时可以使用lldb或者wind ...
 - 反问面试官3个ThreadLocal的问题
			
ThreadLocal,一个Java人面试绕不开的话题,我也很奇怪为什么那些面试官很喜欢问这个,也不知道他们自己有没有搞清楚. 接下来,我想先说说ThreadLocal的用法和使用场景,然后反问面试官 ...
 - 国庆快乐!附ssh实战
			
小伙伴们,有一段时间没更新了,目前在中科院软件所实习,在这里我祝大家国庆快乐! 今天这一期带来ssh命令的实战教程,ssh在工作当中遇到的非常多,因为总是需要登服务器,而且玩法也有不少,这是我常用的几 ...
 - 最小代价的 SSO 单点登录方案
			
现在有多个 WebApp,想用最小的代价实现 SSO 单点登录.所谓最小代价,我的理解就是对原有 WebApp 的改动最小,因此 在旁路增加一个 SsoWebApp 用于管理 SSO 的账号,进行身份 ...
 - 简单粗暴的实现 Blazor Server 登录鉴权
			
既然是简单粗暴,那么就不用关心诸如 IDentityServer4,OAuth 之类的组件,也不使用 AuthenticationStateProvider.IAuthService, razor 页 ...