[译]rabbitmq 2.2 Building from the bottom: queues

时间:2022-03-15 06:07:36

我对rabbitmq学习还不深入,这些翻译仅仅做资料保存,希望不要误导大家。

You have consumers and producers under your belt, and now you’re itching to get

started eh? Not so fast. First, you need to understand queues. Conceptually, there are

three parts to any successful routing of an AMQP message: exchanges, queues, and

bindings. The exchanges are where producers publish their messages, queues are

where the messages end up and are received by consumers, and bindings are how the

messages get routed from the exchange to particular queues. Before you get to examine

exchanges and bindings, you need to understand what a queue is and how it

works. Take a look at figure 2.3.

已经有了consumer和producer的经验,现在已经恨不得要开始了?没那么快。

首先,你需要理解queue。

为了成功routing一个AMQP消息,有3个必须的部分:exchange,queue,binding。

exchange - producer发送消息的地方

queue - message结束,被consumer接收的地方

binding - 消息如何从exchange被路由到queue中

As we said when we were talking about producers and consumers, queues are like

named mailboxes. They’re where messages end up and wait to be consumed. Consumers

receive messages from a particular queue in one of two ways:

queue正如mailbox,是message 结束并且等待被消费的地方。

consumer以下面2个方式从指定的queue中接收消息:

1 By subscribing to it via the basic.consume AMQP command. This will place the

channel being used into a receive mode until unsubscribed from the queue.

While subscribed, your consumer will automatically receive another message

from the queue (as available) after consuming (or rejecting) the last received

message. You should use basic.consume if your consumer is processing many

messages out of a queue and/or needs to automatically receive messages from a

queue as soon as they arrive.

1.通过 basic.consume订阅。在取消订阅之前,channel会被设置为接收模式。

订阅之后,consumer在消费(或拒绝)了最后的收到的消息之后,将会自动收到queue(如果存在)中的消息。

如果你的consumer会处理许多queue之外的消息,并且(或者)需要尽快的自动从queue中接收消息,

那么你应该使用basic.consume设置为订阅。

2 Sometimes, you just want a single message from a queue and don’t need to be

persistently subscribed. Requesting a single message from the queue is done by

using the basic.get AMQP command. This will cause the consumer to receive

the next message in the queue and then not receive further messages until the

next basic.get. You shouldn’t use basic.get in a loop as an alternative to

basic.consume, because it’s much more intensive on Rabbit. basic.get essentially

subscribes to the queue, retrieves a single message, and then unsubscribes

every time you issue the command. High-throughput consumers should always

use basic.consume.

2.有时,你只是想要queue中的单个的消息。

从queue中获取一个消息可以使用basic.get,可以获得queue中的下一条消息。

在再次调用basic.get之前,你不会受到消息。

不要用循环basic.get来代替basic.consume,因为这会很频繁。

basic.get本质上是订阅了一个queue,接收单个消息,然后取消订阅。

高吞吐的consumer应该使用basic.consume。

If one or more consumers are subscribed to a queue, messages are sent immediately

to the subscribed consumers. But what if a message arrives at a queue with no subscribed

consumers? In that case, the message waits in the queue. As soon as a consumer

subscribes to the queue, the message will be sent to that consumer. A more

interesting question is how messages in a queue are distributed when multiple consumers

are subscribed to the same queue.

如果一个或者多个consumer在订阅一个queue,消息会马上发送到订阅的consumer。

但是如果一个消息到达一个没有consumer订阅的queue中会怎样呢?

消息会保存在queue中等待。当consumer订阅了queue,消息会被发送到consumer。

更有意思的问题是:当多个consumer订阅了同一个queue,消息是如何分布的?

When a Rabbit queue has multiple consumers, messages received by the queue are

served in a round-robin fashion to the consumers. Each message is sent to only one

consumer subscribed to the queue. Let’s say you had a queue named seed_bin and consumers

Farmer Bob and Farmer Esmeralda subscribed to seed_bin. As messages arrive

in seed_bin, the deliveries would look like this:

1 Message_A arrives in the seed_bin queue.

2 RabbitMQ sends Message_A to Farmer Bob.

3 Farmer Bob acknowledges receipt of Message_A.

4 RabbitMQ removes Message_A from the seed_bin queue.

5 Message_B arrives in the seed_bin queue.

6 RabbitMQ sends Message_B to Farmer Esmeralda.

7 Farmer Esmeralda acknowledges receipt of Message_B.

8 RabbitMQ removes Message_B from the seed_bin queue.

当一个queue有多个consumer,queue收到消息后会循环发到consumer。

每个消息只会发到一个订阅的consumer。

假设你有一个queue,名字是seed_bin,已订阅的consumer 有 Bob 和 Esmeralda。

当消息到达seed_bin:

1. 消息A到达seed_bin

2. rabbitmq发送消息A到Bob

3. Bob 确认收到消息A

4. rabbitmq将消息A移出队列seed_bin

5. 消息B到达seed_bin

6. rabbitmq发送消息B到Esmeralda

7. Esmeralda 确认收到消息A

8. rabbitmq将消息B移出队列seed_bin

You may have noticed that Farmers Bob and Esmeralda did something we haven’t

talked about yet: they acknowledged receipt of the message. Every message that’s

received by a consumer is required to be acknowledged. Either the consumer must

explicitly send an acknowledgement to RabbitMQ using the basic.ack AMQP command,

or it can set the auto_ack parameter to true when it subscribes to the queue.

When auto_ack is specified, RabbitMQ will automatically consider the message

acknowledged by the consumer as soon as the consumer has received it. An important

thing to remember is that message acknowledgements from the consumer have nothing

to do with telling the producer of the message it was received. Instead, the acknowledgements

are a way for the consumer to confirm to RabbitMQ that the consumer has

correctly received the message and RabbitMQ can safely remove it from the queue.

你好像注意到了一些新东西:"Bob 确认收到消息A"。

每个consumer收到消息后,是需要返回acknowledged的。

可以通过basic.ack设置自动返回,或者程序手动返回。

如果设置了自动返回(auto_ack),当consumer收到消息后会马上返回acknowledged。

需要记住的是,即使consumer返回了acknowledged,也不会告诉producer。

只能代表consumer已经收到了消息,queue可以安全地移除消息了。

If a consumer receives a message and then disconnects from Rabbit (or unsubscribes

from the queue) before acknowledging, RabbitMQ will consider the message

undelivered and redeliver it to the next subscribed consumer. If your app crashes, you

can be assured the message will be sent to another consumer for processing. On the

other hand, if your consumer app has a bug and forgets to acknowledge a message,

Rabbit won’t send the consumer any more messages.

如果consumer收到了消息,但是还没回复acknowledged,然后与rabbitmq断链了(或者取消订阅),

rabbitmq认为消息被没有被正确投递,然后投递到下一个consumer中。

如果程序崩溃了,消息也是会被投递到下一个consumer中。

另一方面,如果你的consumer程序有bug并且忘记返回acknowledged,

rabbitmq将不会再向这个consumer投递任何消息。

This is because Rabbit considers

the consumer not ready to receive another message until it acknowledges the last one

it received. You can use this behavior to your advantage. If processing the contents of

a message is particularly intensive, your app can delay acknowledging the message

until the processing has finished. This will keep Rabbit from overloading you with

more messages than your app can handle.

这是因为rabbitmq认为consumer收到最后一条消息后,如果没有返回acknowledged,

则该consumer处于未准备好的状态。

你可以利用这个特性。

在程序未处理完之前,不要返回acknowledged。

这可以防止rabbitmq向你的程序发送大量的程序导致程序超负荷。

What if you want to specifically reject a message rather than acknowledge it after

you've received it? For example, let’s say that when processing the message you

encounter an uncorrectable error, but it only affects this consumer due to a hardware

issue (this is a good reason to never acknowledge a message until it’s processed). As

long as the message hasn’t been acknowledged yet, you have two options:

1 Have your consumer disconnect from the RabbitMQ server. This will cause

RabbitMQ to automatically requeue the message and deliver it to another consumer.

he advantage to this method is that it works across all versions of

RabbitMQ. The disadvantage is the extra load put on the RabbitMQ server

from the connecting and disconnecting of your consumer (a potentially significant

load if your consumer is encountering errors on every message).

如何拒绝一条收到的消息呢?

假设你的程序处理一条消息时发生了无法修复的错误,但是只是硬件错误。

当消息未被acknowledged,你有2个选择:

1.consumer程序主动与rabbitmq断链,这会导致rabbitmq自动把消息从新放到队列中并

投递给另外一个consumer。

好处是在rabbitmq的任何版本,你都可以这么干。

坏处是导致了额外的断链和建链(如果每条消息都产生这个错误,那这问题很严重)。

2 If you’re running RabbitMQ 2.0.0 or newer, use the basic.reject AMQP command.

basic.reject does exactly what it sounds like: it allows your consumer

to reject a message RabbitMQ has sent it. If you set the requeue parameter of

the reject command to true, RabbitMQ will redeliver the message to the next

subscribed consumer. Setting requeue to false will cause RabbitMQ to remove

the message from the queue immediately without resending it to a new consumer.

You can also discard a message simply by acknowledging it (this method

of discarding has the advantage of working with all versions of RabbitMQ). This

is useful if you detect a malformed message you know none of your consumers

will be able to process.

2. 如果rabbitmq的版本为2.0.0以上,可以使用 basic.reject,

它可以拒绝已经收到的消息。

如果设置为true,rabbitmq会重新投递该消息到其他已订阅的consumer。

如果设置requeue会导致rabbitmq从queue中移除这条消息,不会发送到其他consumer。

当然你也可以回复acknowledging(假丢弃,所有版本通用),这是一个很有用的方法,

例如你收到一个错误的消息,即使投递给其他consumer,别的consumer也无法处理,

那就通过这个方法丢弃他。

NOTE

When discarding a message, why would you want to use the

basic.reject command with the requeue parameter set to false instead of

acknowledging the message? Future versions of RabbitMQ will support a special

“dead letter” queue where messages that are rejected without requeuing

will be placed. A dead letter queue lets you inspect rejected/undeliverable

messages for issues. If you want your app to automatically take advantage of

the dead letter queue feature when it’s added to Rabbit, use the reject command

with requeue set to false.

须知:

为什么丢弃一个消息时,要使用basic.reject而不是直接acknowledging?

因为以后版本的rabbitmq会支持“dead letter” queue,用于存放被reject并且不被

重新放入queue的消息。

你可以通过“dead letter” queue,查看reject或者无法投递的消息。

如果你想自己弄这么一个“dead letter” queue,那么在使用reject的时候,

rqueue属性设置false。

There’s one more important thing you need to know about queues: how to create

them. Both consumers and producers can create queues by using the queue.declare

AMQP command. But consumers can’t declare a queue while subscribed to another

one on the same channel. They must first unsubscribe in order to place the channel

in a “transmit” mode. When creating a queue, you usually want to specify its name.

The name is used by consumers to subscribe to it, and is how you specify the queue

when creating a binding. If you don’t specify a name, Rabbit will assign a random

name for you and return it in the response to the queue.declare command (this is

useful when using temporary “anonymous” queues for RPC-over-AMQP applications, as

you’ll see in chapter 4).

如何创建queue:

所有consumer和producer可以都可以使用queue.declare创建queue。

但是consumer不能在一个channel中订阅一个queue,声明另一个queue。

必须先取消订阅,使channel处于 "transmit" 模式。

当创建一个queue时,可以设置名字。

这个名字,在consumer订阅时会用到,在创建一个binding关系时会用到。

如果在使用queue.declare创建queue时没有指定名字,那么rabbitmq会分配一个随机

名字,并返回。

Here are some other useful properties you can set for the

queue:

以下是你可以设置的queue的一些有用属性:

~exclusiveWhen set to true, your queue becomes private and can only be

consumed by your app. This is useful when you need to limit a queue to only

one consumer.

~exclusive 当设置为true,这个queue是你的程序私有的。单个队列单个consumer。

~auto-deleteThe queue is automatically deleted when the last consumer

unsubscribes. If you need a temporary queue used only by one consumer, combine

auto-delete with exclusive. When the consumer disconnects, the queue

will be removed.

~auto-delete  当最后一个consumer取消订阅,queue会被自动删除。如果你需要

一个只被一个consumer使用的临时queue,设置auto-delete和exclusive,

当consumer断链,queue会被删除。

What happens if you try to declare a queue that already exists? As long as the declaration

parameters match the existing queue exactly, Rabbit will do nothing and return

successfully as though the queue had been created (if the parameters don’t match,

the declaration attempt will fail). If you just want to check whether a queue exists, you

can set the passive option of queue.declare to true. With passive set to true,

queue.declare will return successfully if the queue exists, and return an error without

creating the queue if it doesn’t exist.

如果你声明一个已经存在的queue,会怎么样?

只要声明的参数和已经存在的queue正确匹配,rabbitmq会返回成功,

(如果参数不匹配,会返回失败)。

如果你只是想看下这个queue是否存在,你可以设置queue.declare的passive为true,

这样的话,如果queue存在queue.declare会返回成功,然后返回一个错误,并且如果这个queue

不存在,rabbitmq不会创建它。

When you’re designing your apps, you’ll most likely ask yourself whether your producers

or consumers should create the queues you need. It might seem that the most

natural answer is that your consumers should create your queues. After all, they’re the

ones that need to subscribe, and you can’t subscribe to a queue that doesn’t exist,

right? Not so fast. You need to first ask yourself whether the messages your producers

create can afford to disappear. Messages that get published into an exchange but have

no queue to be routed to are discarded by Rabbit.

当你设计程序时,你需要想清楚你的producer或者consumer是否需要创建queue。

通常来说,你都觉得需要。

毕竟,你需要订阅一个queue,并且你不能订阅一个不存在的queue。

你需要首先问问自己,你的producer能否接受消息丢失?

消息投递到exchange中但是没有对应的queue,导致rabbitmq无法正确路由该消息。

So if you can’t afford for your messages

to be black-holed, both your producers and your consumers should attempt to

create the queues that will be needed. On the other hand, if you can afford to lose

messages or have implemented a way to republish messages that donˇt get processed

(weˇll show you how to do this) you can have only your consumers declare the queues.

Queues are the foundational block of AMQP messaging:

~ They give you a place for your messages to wait to be consumed.

~ Queues are perfect for load balancing. Just attach a bunch of consumers and let

RabbitMQ round-robin incoming messages evenly among them.

~ Theyˇre the final endpoint for any messages in Rabbit (unless they get blackholed).

With queues under your belt, youˇre ready to move to the next building block of

Rabbit: exchanges and bindings!

所以如果你不能接受你的消息被black-holed,

那么你的producer,consumer都应该尝试创建所需的queue。

另一方面,如果你能接受丢消息的问题,或者你有办法重发没被处理的消息,

你可以只在consumer中声明queue。

queue是AMQP消息发送的基础模块:

~queue可以作为消息被消费前存放的地方

~queue可以用于负载均衡。

~queue是消息在rabbitmq中的终点(除非消息被blackholed)