【导读】本文介绍了 go 移步任务队列的实现。
在一些常见的场景中,如果遇到了某些请求特别耗时间,为了不影响其它用户的请求以及节约服务器资源,我们通常会考虑使用异步任务队列去解决,这样可以快速地处理请求、只返回给用户任务创建结果,等待任务完成之后,我们再告知用户任务的完成情况。
对于 golang,我们可以通过 worker pool 异步处理任务,在大多数情况下,如果不在意数据丢失以及服务器性能足够,我们就没有必要考虑别的方案,毕竟这样实现非常简单。
接下来我们先来说说如何用 worker pool 解决异步任务的问题。
worker pool worker pool,也有叫做 goroutine pool 的,都是指用 go channel 以及 goroutine 来实现的任务队列。go channel 本质上就是一个队列,因此用作任务队列是很自然的。
在我们不用 go channel 的时候,我们也许会使用这样的方式来处理异步任务:
for i := 0;i < 100;i++ { go func() { // process job }()} 这样的方式是不推荐的,因为在请求量到达一定程度,系统处理不过来的时候,会造成 goroutine 的爆炸,拖慢整个系统的运行,甚至程序的崩溃,数据也就完全丢失了。
如果我们用简单的方式,可以看看接下来的例子:一个发送者(也叫做生产者),一个接受者(也叫做消费者,或者 worker):
type job struct {...}jobchan := make(chan job)quit := make(chan bool)go func() { select { case job := <-jobchan: case <- quit: return }}()for i := 0;i < 100;i++ { jobchan <- job{...}}close(jobchan)quit <- true 如果 worker 不够,我们可以增加,这样可以并行处理任务:
for i := 0;i < 10;i++ { go func() { for job := range jobchan { // process job } }()} 这样,一个非常简单的 worker pool 就完成了,只是,它对任务的处理还会有问题,比如无法设置超时、无法处理 panic 错误等。
实际上,目前已经有很多的开源库可以帮你实现了,以 worker pool 为关键词在 github 上可以搜到一大堆:
github - jeffail/tunny: a goroutine pool for go github - gammazero/workerpool: concurrency limiting goroutine pool 那么,它们的缺点呢?
很明显,它们的缺点就在于缺乏管理,可以说是完全不管任务的结果,即使我们加日志输出也只是为了简单监控,更要命的就是进程重启的时候,比如进程挂了,或者程序更新,都会导致数据丢失,毕竟生产者与消费者在一个进程中的时候,会互相影响(抢占 cpu 与内存资源)。因此前面我也说了,在不管这两个问题的时候,可以考虑用。
如果数据很重要(实际上,我认为用户上传的业务数据都重要,不能丢失),为了解决这些问题,我们必须换一种解决方案。
分布式异步任务队列 接下来再说说异步的分布式任务队列,要用到这个工具的时候,我们大致有以下几个需求:
分布式:生产者与消费者隔离; 数据持久化:在程序重启的时候,不丢失已有的数据; 任务重试:会有任务偶然失败的场景,重试是最简单的方式,但需要保证任务的执行时是冪等的; 任务延时:延迟执行,比如 5 分钟后给用户发红包; 任务结果的临时存储,可用于储存; 任务处理情况监控:及时发现任务执行出错情况; 对于 python 来说,有个大名鼎鼎的 celery(https://github.com/celery/celery),它完全包含上面的功能。它包含两个比较重要的组件:一个是消息队列,比如 redis/rabbitmq 等,celery 中叫做 broker,然后还需要有数据库,用于存储任务状态,叫做 result backend。
显然对于 go 也有很多不错的开源库,其中一个学 celery 的是 machinery(github.com/richardknop/machinery),它目前能满足大部分需求,而且一直在积极维护,也是我们团队目前在用的。
它目前支持的 broker 有 amqp(rabbitmq)、redis、aws sqs、gcp pub/sub,目前对国内同行来说,rabbitmq 或者 redis 会相对比较合适。
另外它还支持几个高级用法:
groups:允许你定义多个并行的任务,在最后取任务结果的时候,可以一起返回; chords:允许你定义一个回调任务,在 group 任务执行完毕后执行对应的回调任务; chains:允许你定义串行执行的任务,任务将会被串行执行; 说了优点,再说说它的缺点:
任务监控支持不够,目前只有分布式追踪 opentracing 的支持,假如我要使用 prometheus,会比较困难,它的自定义错误处理过于简单,连上下文都不给你; 传入的参数目前只支持非常简单的参数,不支持 struct、map,还得定义参数的类型,这样的方式会将这个库限制在 golang 世界中,而无法拓展适用于其它语言; p.s. 其实对于 goroutine 的方案,在以下两种情况下,可以考虑使用:
必须同步返回给用户请求结果; 服务器资源足够,仅仅用 worker pool 就能降低请求的响应时长到可接受范围; 这两种方案都会返回请求结果,失败的情况下靠客户端重新请求来解决数据丢失的问题。
原文标题:golang 中的异步任务队列
文章出处:【微信公众号:linux爱好者】欢迎添加关注!文章转载请注明出处。
3只人工智能股年内股价翻倍_为啥富瀚微/海康威视/科大讯飞这么猛
家用投影机安装使用答疑
贵州警用无人机试飞成功
变频器驱动板主要集成驱动IGBT电路的信号放大板作用及功能
十万片晶圆报废重做,让台积电本季营收无法达成原预估目标
如何用Worker pool解决异步任务的问题
币圈大鹏:新手入门怎么快速了解数字货币
锂离子电池充电器电路图解析
基于一个全球性的加密货币社区融资平台Adel介绍
半导体:十年产业投资大机会,我国集成电路市场增速全球第一
融合一二代优点 日本开发出第三代OLED荧光材料
酷派集团发布公告,宣称公司与浦发贷款纠纷案达成和解
Linux PWM开发指南
几种常用单片机介绍
几个方面有针对性地解决当下国内智慧城市建设中的各种问题
LVDS技术原理和设计简介
电源模块布局中考虑元器件的寄生参数
人民时评:挖掘机器人产业更大潜力
用HDL代码描述加法运算要用操作符“+” 看似很简单实则不然
焊接换热器需要具备哪些技能