go-machinery入门教程(异步任务队列教程)

前言

哈喽,大家好,我是asong,这次给大家介绍一个go的异步任务框架machinery。使用过python的同学们都知道Celery框架,machinery框架就类似于Celery框架。下面我们就来学习一下machinery的基本使用。

自己翻译一个粗略版的machinery中文文档,有需要的伙伴们公众号自取无水印版:后台回复:machinery即可领取。

或者github下载:https://github.com/asong2020/Golang_Dream/tree/master/machinery

抛砖引玉

我们在使用某些APP时,登陆系统后一般会收到一封邮件或者一个短信提示我们在某个时间某某地点登陆了。而邮件或短信都是在我们已经登陆后才收到,这里就是采用的异步机制。大家有没有想过这里为什么没有使用同步机制实现呢?我们来分析一下。假设我们现在采用同步的方式实现,用户在登录时,首先会去检验一下账号密码是否正确,验证通过后去给用户发送登陆提示信息,假如在这一步出错了,那么就会导致用户登陆失败,这样是大大影响用户的体验感的,一个登陆提示的优先级别并不是很高,所以我们完全可以采用异步的机制实现,即使失败了也不会影响用户的体验。前面说了这么多,那么异步机制该怎么实现呢?对,没错,就是machinery框架,听说你们还不会使用它,今天我就写一个小例子,我们一起来学习一下他吧。

特性

上面只是简单举了个例子,任务队列有着广泛的应用场景,比如大批量的计算任务,当有大量数据插入,通过拆分并分批插入任务队列,从而实现串行链式任务处理或者实现分组并行任务处理,提高系统鲁棒性,提高系统并发度;或者对数据进行预处理,定期的从后端存储将数据同步到到缓存系统,从而在查询请求发生时,直接去缓存系统中查询,提高查询请求的响应速度。适用任务队列的场景有很多,这里就不一一列举了。回归本文主题,既然我们要学习machinery,就要先了解一下他都有哪些特性呢?

  • 任务重试机制
  • 延迟任务支持
  • 任务回调机制
  • 任务结果记录
  • 支持Workflow模式:Chain,Group,Chord
  • 多Brokers支持:Redis, AMQP, AWS SQS
  • 多Backends支持:Redis, Memcache, AMQP, MongoDB

架构

任务队列,简而言之就是一个放大的生产者消费者模型,用户请求会生成任务,任务生产者不断的向队列中插入任务,同时,队列的处理器程序充当消费者不断的消费任务。基于这种框架设计思想,我们来看下machinery的简单设计结构图例:

  • Sender:业务推送模块,生成具体任务,可根据业务逻辑中,按交互进行拆分;
  • Broker:存储具体序列化后的任务,machinery中目前支持到Redis, AMQP,和SQS;
  • Worker:工作进程,负责消费者功能,处理具体的任务;
  • Backend:后端存储,用于存储任务执行状态的数据;

e.g

学习一门新东西,我都习惯先写一个demo,先学会了走,再学会跑。所以先来看一个例子,功能很简单,异步计算1到10的和。

先看一下配置文件代码:

broker: redis://localhost:6379

default_queue: "asong"

result_backend: redis://localhost:6379

redis:
  max_idle: 3
  max_active: 3
  max_idle_timeout: 240
  wait: true
  read_timeout: 15
  write_timeout: 15
  connect_timeout: 15
  normal_tasks_poll_period: 1000
  delayed_tasks_poll_period: 500
  delayed_tasks_key: "asong"

这里brokerresult_backend来实现。

主代码,完整版github获取:


func main()  {

	cnf,err := config.NewFromYaml("./config.yml",false)
	if err != nil{
		log.Println("config failed",err)
		return
	}

	server,err := machinery.NewServer(cnf)
	if err != nil{
		log.Println("start server failed",err)
		return
	}

	// 注册任务
	err = server.RegisterTask("sum",Sum)
	if err != nil{
		log.Println("reg task failed",err)
		return
	}

	worker := server.NewWorker("asong", 1)
	go func() {
		err = worker.Launch()
		if err != nil {
			log.Println("start worker error",err)
			return
		}
	}()

	//task signature
	signature := &tasks.Signature{
		Name: "sum",
		Args: []tasks.Arg{
			{
				Type:  "[]int64",
				Value: []int64{1,2,3,4,5,6,7,8,9,10},
			},
		},
	}

	asyncResult, err := server.SendTask(signature)
	if err != nil {
		log.Fatal(err)
	}
	res, err := asyncResult.Get(1)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("get res is %v\n", tasks.HumanReadableResults(res))

}

运行结果:

INFO: 2020/10/31 11:32:15 file.go:19 Successfully loaded config from file ./config.yml
INFO: 2020/10/31 11:32:15 worker.go:58 Launching a worker with the following settings:
INFO: 2020/10/31 11:32:15 worker.go:59 - Broker: redis://localhost:6379
INFO: 2020/10/31 11:32:15 worker.go:61 - DefaultQueue: asong
INFO: 2020/10/31 11:32:15 worker.go:65 - ResultBackend: redis://localhost:6379
INFO: 2020/10/31 11:32:15 redis.go:100 [*] Waiting for messages. To exit press CTRL+C
DEBUG: 2020/10/31 11:32:16 redis.go:342 Received new message: {"UUID":"task_9f01be1f-3237-49f1-8464-eecca2e50597","Name":"sum","RoutingKey":"asong","ETA":null,"GroupUUID":"","GroupTaskCount":0,"Args":[{"Name":"","Type":"[]int64","Value":[1,2,3,4,5,6,7,8,9,10]}],"Headers":{},"Priority":0,"Immutable":false,"RetryCount":0,"RetryTimeout":0,"OnSuccess":null,"OnError":null,"ChordCallback":null,"BrokerMessageGroupId":"","SQSReceiptHandle":"","StopTaskDeletionOnError":false,"IgnoreWhenTaskNotRegistered":false}
DEBUG: 2020/10/31 11:32:16 worker.go:261 Processed task task_9f01be1f-3237-49f1-8464-eecca2e50597. Results = 55
2020/10/31 11:32:16 get res is 55

好啦,现在我们开始讲一讲上面的代码流程,

  • 读取配置文件,这一步是为了配置brokerresult_backend,这里我都选择的是redis,因为电脑正好有这个环境,就直接用了。
  • Machinery 库必须在使用前实例化。实现方法是创建一个Server实例。ServerMachinery配置和注册任务的基本对象。
  • 在你的workders能消费一个任务前,你需要将它注册到服务器。这是通过给任务分配一个唯一的名称来实现的。
  • 为了消费任务,你需有有一个或多个worker正在运行。运行worker所需要的只是一个具有已注册任务的Server实例。每个worker将只使用已注册的任务。对于队列中的每个任务,Worker.Process()方法将在一个goroutine中运行。可以使用server.NewWorker的第二参数来限制并发运行的worker.Process()调用的数量(每个worker)。
  • 可以通过将Signature实例传递给Server实例来调用任务。
  • 调用HumanReadableResults这个方法可以处理反射值,获取到最终的结果。

多功能

1. 延时任务

上面的代码只是一个简单machinery使用示例,其实machiney也支持延时任务的,可以通过在任务signature上设置ETA时间戳字段来延迟任务。

eta := time.Now().UTC().Add(time.Second * 20)
	signature.ETA = &eta

2. 重试任务

在将任务声明为失败之前,可以设置多次重试尝试。斐波那契序列将用于在一段时间内分隔重试请求。这里可以使用两种方法,第一种直接对tsak signature中的retryTimeoutRetryCount字段进行设置,就可以,重试时间将按照斐波那契数列进行叠加。

//task signature
	signature := &tasks.Signature{
		Name: "sum",
		Args: []tasks.Arg{
			{
				Type:  "[]int64",
				Value: []int64{1,2,3,4,5,6,7,8,9,10},
			},
		},
		RetryTimeout: 100,
		RetryCount: 3,
	}

或者,你可以使用return.tasks.ErrRetryTaskLater 返回任务并指定重试的持续时间。

func Sum(args []int64) (int64, error) {
	sum := int64(0)
	for _, arg := range args {
		sum += arg
	}

	return sum, tasks.NewErrRetryTaskLater("我说他错了", 4 * time.Second)

}

3. 工作流

上面我们讲的都是运行一个异步任务,但是我们往往做项目时,一个需求是需要多个异步任务以编排好的方式执行的,所以我们就可以使用machinery的工作流来完成。

3.1 Groups

Group 是一组任务,它们将相互独立地并行执行。还是画个图吧,这样看起来更明了:

一起来看一个简单的例子:

	// group
	group,err :=tasks.NewGroup(signature1,signature2,signature3)
	if err != nil{
		log.Println("add group failed",err)
	}

	asyncResults, err :=server.SendGroupWithContext(context.Background(),group,10)
	if err != nil {
		log.Println(err)
	}
	for _, asyncResult := range asyncResults{
		results,err := asyncResult.Get(1)
		if err != nil{
			log.Println(err)
			continue
		}
		log.Printf(
			"%v  %v  %v\n",
			asyncResult.Signature.Args[0].Value,
			tasks.HumanReadableResults(results),
		)
	}

group中的任务是并行执行的。

3.2 chrods

我们在做项目时,往往会有一些回调场景,machiney也为我们考虑到了这一点,Chord允许你定一个回调任务在groups中的所有任务执行结束后被执行。

来看一段代码:

callback := &tasks.Signature{
		Name: "call",
	}



	group, err := tasks.NewGroup(signature1, signature2, signature3)
	if err != nil {

		log.Printf("Error creating group: %s", err.Error())
		return
	}

	chord, err := tasks.NewChord(group, callback)
	if err != nil {
		log.Printf("Error creating chord: %s", err)
		return
	}

	chordAsyncResult, err := server.SendChordWithContext(context.Background(), chord, 0)
	if err != nil {
		log.Printf("Could not send chord: %s", err.Error())
		return
	}

	results, err := chordAsyncResult.Get(time.Duration(time.Millisecond * 5))
	if err != nil {
		log.Printf("Getting chord result failed with error: %s", err.Error())
		return
	}
	log.Printf("%v\n", tasks.HumanReadableResults(results))

上面的例子并行执行task1、task2、task3,聚合它们的结果并将它们传递给callback任务。

3.3 chains

chain就是一个接一个执行的任务集,每个成功的任务都会触发chain中的下一个任务。

看这样一段代码:

//chain
	chain,err := tasks.NewChain(signature1,signature2,signature3,callback)
	if err != nil {

		log.Printf("Error creating group: %s", err.Error())
		return
	}
	chainAsyncResult, err := server.SendChainWithContext(context.Background(), chain)
	if err != nil {
		log.Printf("Could not send chain: %s", err.Error())
		return
	}

	results, err := chainAsyncResult.Get(time.Duration(time.Millisecond * 5))
	if err != nil {
		log.Printf("Getting chain result failed with error: %s", err.Error())
	}
	log.Printf(" %v\n", tasks.HumanReadableResults(results))

上面的例子执行task1,然后是task2,然后是task3。当一个任务成功完成时,结果被附加到chain中下一个任务的参数列表的末尾,最终执行callback任务。

文中代码地址:https://github.com/asong2020/Golang_Dream/tree/master/machinery/example

总结

这一篇文章到这里就结束了,machinery还有很多用法,比如定时任务、定时任务组等等,就不在这一篇文章介绍了。更多使用方法解锁可以观看machinery文档。因为machiney没有中文文档,所以我在学习的过程自己翻译了一篇中文文档,需要的小伙伴们自取。

获取步骤:关注公众号【Golang梦工厂】,后台回复:machiney即可获取无水印版~~~

好啦,这一篇文章到这就结束了,我们下期见~~。希望对你们有用,又不对的地方欢迎指出,可添加我的golang交流群,我们一起学习交流。

结尾给大家发一个小福利吧,最近我在看[微服务架构设计模式]这一本书,讲的很好,自己也收集了一本PDF,有需要的小伙可以到自行下载。获取方式:关注公众号:[Golang梦工厂],后台回复:[微服务],即可获取。

我翻译了一份GIN中文文档,会定期进行维护,有需要的小伙伴后台回复[gin]即可下载。

我是asong,一名普普通通的程序猿,让gi我一起慢慢变强吧。我自己建了一个golang交流群,有需要的小伙伴加我vx,我拉你入群。欢迎各位的关注,我们下期见~~~

推荐往期文章:

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 《电力机械》是一本广泛使用的教材,由Fitzgerald, Kingsley和Uman合著。这本书是电力工程领域的经典教材,已经出版了6个版本。它主要介绍了电力机械的基本原理、工作原理和应用。 这本书首先详细介绍了电力机械的基本知识,包括电机和发电机的原理和分类,电动机的结构和工作原理,磁场与电动力之间的关系等。在讲解过程中,作者使用了简明易懂的语言和生动的实例,使读者更容易理解和掌握知识。 书中还涵盖了电力机械的应用领域,比如电力系统中的发电机和变压器,工业领域的电机和发电机,以及交通运输中的电动车辆。通过详细讲解这些应用,读者可以了解到电力机械在不同领域的实际应用和重要性。 除了基础知识和应用,这本书还介绍了电力机械的设计和维护。读者可以学习到电力机械的设计原则和方法,以及维护和故障排除的技巧。这些内容对于电力工程师和维护人员来说都是非常有用的。 总之,《电力机械》这本书是一本很好的教材,它详细而全面地介绍了电力机械的基本知识、应用和设计。它的编写风格简明易懂,适合各种读者使用。无论是作为学习教材还是作为参考书,这本书都是电力工程师必备的工具。 ### 回答2: 《电动机6版》是由Fitzgerald, Kingsley和Uman共同编写的一本关于电动机的教材。本书旨在深入介绍电动机的原理、结构和应用,适合电气工程领域的学习者和从业者。 首先,本书从基本原理入手,介绍了电动机的工作原理、转子、定子、磁路等基本概念和组成部分。通过深入浅出的解释和丰富的图表,读者可以对电动机的结构和工作方式有一个清晰的了解。 其次,本书涵盖了各种不同类型的电动机,包括直流电动机、交流电动机和步进电动机等。对于不同种类的电动机,书中详细介绍了其特点、应用和控制方法,帮助读者理解不同种类电动机的工作原理和适用情况。 此外,本书还着重介绍了电动机的性能和效率评估方法,以及电动机的控制和保护技术。这部分内容对于学习者和从业者来说都非常重要,因为电动机的性能评估和控制是电气工程领域中必不可少的技能。 最后,本书还提供了大量的习题和实例,以帮助读者巩固所学知识。这些习题和实例覆盖了各种不同的应用场景和问题,让读者能够将理论知识应用到实际工作中。 总的来说,《电动机6版》是一本内容丰富、结构严谨的电动机教材。它既适合初学者学习电动机的基础知识,又适合从业者提高专业技能。无论是对于学术研究还是实际应用,这本书都是一本不可或缺的参考书籍。 ### 回答3: 《Electric Machinery》(第六版)是一本由Fitzgerald、Kingsley和Uman合著的电机书籍。该书是电机工程师和学生的教材,旨在提供关于电动机和发电机的全面和深入的知识。本书具有以下特点和优势。 首先,本书内容全面,覆盖了电机的各个方面,包括电机原理、磁场和电磁场理论、电机和变压器的等效电路模型、感应电动机、直流电动机、同步电动机、步进电动机等。读者可以通过本书全面地了解电机的原理、工作原理和性能分析方法。 其次,本书注重理论与实践相结合。书中不仅提供了理论知识,还包括大量的实际案例、计算示例和练习题,帮助读者将理论应用到实际工程问题中。这种理论与实践相结合的方法有助于读者更好地理解和掌握电机的工作原理和设计方法。 此外,本书还注重图示和图表的使用。丰富的图示和图表有助于读者更直观地了解电机的结构、原理和性能。同时,图表中的数据和曲线也提供了重要的参考信息,便于读者进行电机的分析和计算。 最后,本书通俗易懂,语言简练。作者使用简洁明了的语言,避免了复杂的数学推导,使得读者能够更轻松地理解电机的工作原理和设计方法。 总之,《Electric Machinery》(第六版)是一本详细全面的电机教材,适用于电机工程师和学生学习和研究电机相关知识。无论是初学者还是专业人士,都能从本书中获得有益的信息和启发,提升对电机的理解和应用能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值