源文来自 RabbitMQ 英文官网的教程(2.Work Queues),其示例代码采用了 .NET C# 语言。
In the first tutorial we wrote programs to send and receive messages from a named queue. In this one we'll create a Work Queue that will be used to distribute time-consuming tasks among multiple workers.
在第一篇教程中,我们编写了程序从一个具名(已明确命名的)队列中发送和接收消息。在这一篇中,我们会在多个工作单元之间创建一个工作队列来分配耗时的任务。
The main idea behind Work Queues (aka: Task Queues) is to avoid doing a resource-intensive task immediately and having to wait for it to complete. Instead we schedule the task to be done later. We encapsulate a task as a message and send it to a queue. A worker process running in the background will pop the tasks and eventually execute the job. When you run many workers the tasks will be shared between them.
工作队列(又称:任务队列)背后的主旨是为了避免立即执行一项资源密集型任务,从而导致不得不等待它的完成。相反,我们安排任务稍后再完成。我们将任务封装成一个消息并发送给队列,由一个后台工作进程负责弹出该任务并最终执行这项工作。如果有多项工作单元在同时运行,这些任务会在它们之间平均分配。
This concept is especially useful in web applications where it's impossible to handle a complex task during a short HTTP request window.
上述这一概念在 Web 应用程序中尤其有用,因为在一个简短的 HTTP 请求视窗中几乎不可能处理一项复杂任务。
Preparation 准备事宜In the previous part of this tutorial we sent a message containing "Hello World!". Now we'll be sending strings that stand for complex tasks. We don't have a real-world task, like images to be resized or pdf files to be rendered, so let's fake it by just pretending we're busy - by using the Thread.Sleep() function (you will need to add using System.Threading; near the top of the file to get access to the threading APIs). We'll take the number of dots in the string as its complexity; every dot will account for one second of "work". For example, a fake task described by Hello... will take three seconds.
在本教程的之前部分,我们发送了一个包含"Hello World!"的消息。现在,我们将要发送一个代表复杂任务的字符串。我们并没有一个现实世界的任务,诸如图片尺寸修整,或者 pdf 文件的渲染,所以让我们通过伪造忙碌来模拟它,使用 Thread.Sleep() 函数即可(你需要在文件的顶部追加 using System.Threading 命名空间)。我们会采取点的字符数量来表达复杂性,每一个点将表明一秒钟的“工作”,比如模拟任务被描述为“Hello...”时就表示运行了 3 秒钟。
We will slightly modify the Send program from our previous example, to allow arbitrary messages to be sent from the command line. This program will schedule tasks to our work queue, so let's name it NewTask:
我们会在之前的示例中轻微地修改发送程序,以允许任意的消息可以从命令行中被发送。该程序会安排任务至工作队列,所以让我们给它命名为 NewTask 吧:
Like tutorial one we need to generate two projects.
如同第一篇教程,我们需要生成两个工程项目。
dotnet new console --name NewTask mv NewTask/Program.cs NewTask/NewTask.cs dotnet new console --name Worker mv Worker/Program.cs Worker/Worker.cs cd NewTask dotnet add package RabbitMQ.Client dotnet restore cd ../Worker dotnet add package RabbitMQ.Client dotnet restore var message = GetMessage(args); var body = Encoding.UTF8.GetBytes(message); var properties = channel.CreateBasicProperties(); properties.Persistent = true; channel.BasicPublish(exchange: "", routingKey: "task_queue", basicProperties: properties, body: body);Some help to get the message from the command line argument:
一些从命令行参数获取消息的帮助类:
private static string GetMessage(string[] args) { return ((args.Length > 0) ? string.Join(" ", args) : "Hello World!"); }Our old Receive.cs script also requires some changes: it needs to fake a second of work for every dot in the message body. It will handle messages delivered by RabbitMQ and perform the task, so let's copy it to the Worker project and modify:
我们在旧的 Receive.cs 代码中也需要做一些改变:它需要在消息体中针对每一个点模拟一秒钟刻度的工作,同时会处理经由 RabbitMQ 递送过来的消息以及运行任务,所以让我们先把代码复制到工程项目,并做一些修改:
var consumer = new EventingBasicConsumer(channel); consumer.Received += (model, ea) => { var body = ea.Body; var message = Encoding.UTF8.GetString(body); Console.WriteLine(" [x] Received {0}", message); int dots = message.Split('.').Length - 1; Thread.Sleep(dots * 1000); Console.WriteLine(" [x] Done"); }; channel.BasicConsume(queue: "task_queue", autoAck: true, consumer: consumer);Our fake task to simulate execution time:
我们开始模拟一下仿真执行时间:
int dots = message.Split('.').Length - 1; Thread.Sleep(dots * 1000); Round-robin dispatching 循环分发One of the advantages of using a Task Queue is the ability to easily parallelise work. If we are building up a backlog of work, we can just add more workers and that way, scale easily.