RabbitMQ地址:https://www.rabbitmq.com/tutorials/tutorial-three-objectivec.html
中文:
(使用Objective-C客户端)
先决条件
本教程假定RabbitMQ已在标准端口(5672)的本地主机上安装并运行。如果您使用其他主机,端口或凭据,则连接设置需要进行调整。
在哪里获得帮助
如果您在阅读本教程时遇到困难,可以通过邮件列表 或RabbitMQ社区Slack与我们 联系。
在上一个教程中,我们创建了一个工作队列。工作队列背后的假设是,每个任务都恰好交付给一个工人。在这一部分中,我们将做一些完全不同的事情-我们将消息传达给多个消费者。这种模式称为“发布/订阅”。
为了说明这种模式,我们将构建一个简单的日志记录系统。它由两个程序组成-第一个程序将发出日志消息,第二个程序将接收并打印它们。
在我们的日志记录系统中,接收器的每个运行副本都将收到消息。这样,我们将能够运行一个接收器并将日志定向到磁盘。同时我们将能够运行另一个接收器并在屏幕上查看日志。
本质上,已发布的日志消息将被广播到所有接收者。
交流交流
在本教程的前面部分中,我们向队列发送消息和从队列接收消息。现在是时候在Rabbit中引入完整的消息传递模型了。
让我们快速回顾一下先前教程中介绍的内容:
- 甲生产者是发送消息的用户的应用程序。
- 甲队列是一个缓冲区,用于存储消息。
- 甲消费者是接收消息的用户的应用程序。
RabbitMQ消息传递模型的核心思想是生产者从不将任何消息直接发送到队列。实际上,生产者通常甚至根本不知道是否将消息传递到任何队列。
相反,生产者只能将消息发送到交换机。交流是一件非常简单的事情。一方面,它接收来自生产者的消息,另一方面,将它们推入队列。交易所必须确切知道如何处理收到的消息。是否应将其附加到特定队列?是否应该将其附加到许多队列中?还是应该将其丢弃。规则由交换类型定义 。
有几种交换类型可用:direct,topic,headers 和fanout。我们将集中讨论最后一个-扇出。让我们创建这种类型的交换,并将其称为log:
<pre class="lang-objectivec hljs" style="font-style: normal; font-variant-caps: normal; text-indent: 0px; text-transform: none; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-variant-ligatures: normal; font-weight: 500; letter-spacing: 0.16px; orphans: 2; text-align: left; widows: 2; text-decoration-thickness: initial; background-position: initial initial; background-repeat: initial initial;">[ch fanout:@“ logs” ]; </pre>
扇出交换非常简单。正如您可能从名称中猜到的那样,它只是将接收到的所有消息广播到它知道的所有队列中。而这正是我们记录器所需要的。
上市交易所
要列出服务器上的交换,您可以运行非常有用的rabbitmqctl:
<pre class="lang-bash hljs" style="display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; background-position: initial initial; background-repeat: initial initial;">须藤rabbitmqctl list_exchanges </pre>
在此列表中,将有一些amq。*交换和默认(未命名)交换。这些是默认创建的,但是您现在不太可能需要使用它们。
默认交换
在本教程的前面部分中,我们对交换一无所知,但仍然能够将消息发送到队列。这是可能的,因为我们使用的是默认交换,该交换由空字符串(“”)标识。
回想一下我们之前如何发布消息:
<pre class="lang-objectivec hljs" style="display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; background-position: initial initial; background-repeat: initial initial;">[ch.defaultExchange发布:@“ hello” routingKey:@“ hello”持久性:是]; </pre>
在这里,我们使用默认或无名称交换:消息将以routingKey指定的名称路由到队列(如果存在)。
现在,我们可以改为发布到我们的命名交易所:
<pre class="lang-objectivec hljs" style="font-style: normal; font-variant-caps: normal; text-indent: 0px; text-transform: none; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-variant-ligatures: normal; font-weight: 500; letter-spacing: 0.16px; orphans: 2; text-align: left; widows: 2; text-decoration-thickness: initial; background-position: initial initial; background-repeat: initial initial;">RMQExchange * x = [ch扇出:@“ logs” ]; [x publish:[msg dataUsingEncoding:NSUTF8StringEncoding ]]; </pre>
临时队列
您可能还记得,我们使用的是具有特定名称的队列(还记得hello和task_queue吗?)。能够命名队列对我们来说至关重要-我们需要将工人指向同一队列。当您想在生产者和消费者之间共享队列时,给队列命名很重要。
但这不是我们的记录器的情况。我们希望听到所有日志消息,而不仅仅是它们的一部分。我们也只对当前正在发送的消息感兴趣,而对旧消息不感兴趣。为了解决这个问题,我们需要两件事。
首先,无论何时我们连接到Rabbit,我们都需要一个全新的空队列。为此,我们可以创建一个具有随机名称的队列,或者甚至更好-让库为我们选择一个随机队列名称(其他客户端将这项工作留给服务器,但由于Objective-C客户端旨在避免阻塞调用线程,它更喜欢生成自己的名称)。
其次,一旦我们断开了使用者的连接,队列将被自动删除。
在Objective-C客户端中,当我们以空字符串形式提供队列名称时,我们将使用生成的名称创建一个非持久队列:
<pre style="border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px;">RMQQueue * q = [ ch 队列:@“” 选项:RMQQueueDeclareExclusive ];
</pre>
当方法返回时,队列实例包含由库生成的随机队列名称。例如,它可能看起来像 rmq-objc-client.gen-049F8D0B-F330-4D65-9277-0F418F529A93-41604-000030FB39652E07。
当声明它的连接关闭时,该队列将被删除,因为它被声明为独占。您可以在队列指南中了解有关排他标志和其他队列属性的更多信息。
装订
我们已经创建了一个扇出交换和一个队列。现在,我们需要告诉交换机将消息发送到我们的队列。交换和队列之间的关系称为绑定。
<pre style="border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px;">[ q 绑定:x ];
</pre>
从现在开始,日志交换将消息添加到我们的队列中。
列表绑定
您可以猜到它使用rabbitmqctl list_bindings列出现有的绑定 。
全部放在一起
产生日志消息的producer方法与上一教程看起来没有太大不同。最重要的变化是我们现在希望将消息发布到日志交换器,而不是无名的消息交换器。这是emitLog的代码 :
<pre class="lang-objectivec hljs" style="font-style: normal; font-variant-caps: normal; text-indent: 0px; text-transform: none; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-variant-ligatures: normal; font-weight: 500; letter-spacing: 0.16px; orphans: 2; text-align: left; widows: 2; text-decoration-thickness: initial; background-position: initial initial; background-repeat: initial initial;">RMQConnection * conn = [[RMQConnection alloc] initWithDelegate:[RMQConnectionDelegateLogger new]]; [conn start];
id <RMQChannel> ch = [conn createChannel]; RMQExchange * x = [ch扇出:@“ logs” ];
NSString * msg = @“ Hello World!” ;
[x publish:[msg dataUsingEncoding:NSUTF8StringEncoding ]]; NSLog(@“ Sent%@”,msg);
[conn close];
</pre>
如您所见,建立连接后,我们声明了交换。因为禁止发布到不存在的交换,所以此步骤是必需的。
如果没有队列绑定到交换,则消息将丢失,但这对我们来说是可以的。如果没有消费者在听,我们可以放心地丢弃该消息。
receiveLogs的代码:
<pre class="lang-objectivec hljs" style="font-style: normal; font-variant-caps: normal; text-indent: 0px; text-transform: none; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-variant-ligatures: normal; font-weight: 500; letter-spacing: 0.16px; orphans: 2; text-align: left; widows: 2; text-decoration-thickness: initial; background-position: initial initial; background-repeat: initial initial;">RMQConnection * conn = [[RMQConnection alloc] initWithDelegate:[RMQConnectionDelegateLogger new]]; [conn start];
id <RMQChannel> ch = [conn createChannel]; RMQExchange * x = [ch扇出:@“ logs” ]; RMQQueue * q = [ch队列:@“”选项:RMQQueueDeclareExclusive];
[q bind:x];
NSLog(@“等待日志。”);
[q订阅:^(RMQMessage * _Nonnull消息){
NSLog(@“ Received%@”,[[ NSString alloc] initWithData:message.body编码:NSUTF8StringEncoding ]); }];
</pre>
使用rabbitmqctl list_bindings,您可以验证代码是否确实根据需要创建了绑定和队列。在 运行两个receive_logs.rb程序后,您应该看到类似以下内容:
<pre class="lang-bash hljs" style="font-style: normal; font-variant-caps: normal; text-indent: 0px; text-transform: none; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-variant-ligatures: normal; font-weight: 500; letter-spacing: 0.16px; orphans: 2; text-align: left; widows: 2; text-decoration-thickness: initial; background-position: initial initial; background-repeat: initial initial;">sudo rabbitmqctl list_bindings #=>列表绑定...
#=>日志交换amq.gen-JzTY20BRgKO-HjmUJj0wLg队列[]
#=>日志交换amq.gen-vso0PVvyiRIL2WoV3i48Yg队列[]
#=> ...完成。
</pre>
结果的解释很简单:交换日志中的数据进入具有生成名称的两个队列。这正是我们的意图。
要了解如何侦听消息的子集,让我们继续学习 教程4
生产非适用性免责声明
请记住,本教程和其他教程都是教程。它们一次展示一个新概念,并且可能有意过分简化了某些事情,而忽略了其他事情。例如,为简洁起见,很大程度上省略了诸如连接管理,错误处理,连接恢复,并发和度量收集之类的主题。此类简化的代码不应视为已准备就绪。
在使用您的应用程序之前,请先阅读其余文档。我们特别推荐以下指南:发布者确认和消费者确认, 生产清单和监控。
获取帮助并提供反馈
如果您对本教程的内容或与RabbitMQ有关的任何其他主题有疑问,请随时在RabbitMQ邮件列表中询问。
帮助我们改善文档<3
如果您想对该网站做出贡献,可以在GitHub上找到其来源。只需派生存储库并提交拉取请求。谢谢!
英文:
Publish/Subscribe
(using the Objective-C client)
Prerequisites
This tutorial assumes RabbitMQ is installed and running on localhost on the standard port (5672). In case you use a different host, port or credentials, connections settings would require adjusting.
Where to get help
If you're having trouble going through this tutorial you can contact us through the mailing list or RabbitMQ community Slack.
In the previous tutorial we created a work queue. The assumption behind a work queue is that each task is delivered to exactly one worker. In this part we'll do something completely different -- we'll deliver a message to multiple consumers. This pattern is known as "publish/subscribe".
To illustrate the pattern, we're going to build a simple logging system. It will consist of two programs -- the first will emit log messages and the second will receive and print them.
In our logging system every running copy of the receiver will get the messages. That way we'll be able to run one receiver and direct the logs to disk; and at the same time we'll be able to run another receiver and see the logs on the screen.
Essentially, published log messages are going to be broadcast to all the receivers.
Exchanges
In previous parts of the tutorial we sent and received messages to and from a queue. Now it's time to introduce the full messaging model in Rabbit.
Let's quickly go over what we covered in the previous tutorials:
- A producer is a user application that sends messages.
- A queue is a buffer that stores messages.
- A consumer is a user application that receives messages.
The core idea in the messaging model in RabbitMQ is that the producer never sends any messages directly to a queue. Actually, quite often the producer doesn't even know if a message will be delivered to any queue at all.
Instead, the producer can only send messages to an exchange. An exchange is a very simple thing. On one side it receives messages from producers and the other side it pushes them to queues. The exchange must know exactly what to do with a message it receives. Should it be appended to a particular queue? Should it be appended to many queues? Or should it get discarded. The rules for that are defined by the exchange type.
There are a few exchange types available: direct, topic, headers and fanout. We'll focus on the last one -- the fanout. Let's create an exchange of this type, and call it logs:
<pre class="lang-objectivec hljs" style="font-style: normal; font-variant-caps: normal; text-indent: 0px; text-transform: none; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-variant-ligatures: normal; font-weight: 500; letter-spacing: 0.16px; orphans: 2; text-align: left; widows: 2; text-decoration-thickness: initial; background-position: initial initial; background-repeat: initial initial;">[ch fanout:@"logs"];
</pre>
The fanout exchange is very simple. As you can probably guess from the name, it just broadcasts all the messages it receives to all the queues it knows. And that's exactly what we need for our logger.
Listing exchanges
To list the exchanges on the server you can run the ever useful rabbitmqctl:
<pre class="lang-bash hljs" style="display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; background-position: initial initial; background-repeat: initial initial;">sudo rabbitmqctl list_exchanges
</pre>In this list there will be some amq.* exchanges and the default (unnamed) exchange. These are created by default, but it is unlikely you'll need to use them at the moment.
The default exchange
In previous parts of the tutorial we knew nothing about exchanges, but still were able to send messages to queues. That was possible because we were using a default exchange, which is identified by the empty string ("").
Recall how we published a message before:
<pre class="lang-objectivec hljs" style="display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; background-position: initial initial; background-repeat: initial initial;">[ch.defaultExchange publish:@"hello" routingKey:@"hello" persistent:YES];
</pre>Here we use the default or nameless exchange: messages are routed to the queue with the name specified by routingKey, if it exists.
Now, we can publish to our named exchange instead:
<pre class="lang-objectivec hljs" style="font-style: normal; font-variant-caps: normal; text-indent: 0px; text-transform: none; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-variant-ligatures: normal; font-weight: 500; letter-spacing: 0.16px; orphans: 2; text-align: left; widows: 2; text-decoration-thickness: initial; background-position: initial initial; background-repeat: initial initial;">RMQExchange *x = [ch fanout:@"logs"];
[x publish:[msg dataUsingEncoding:NSUTF8StringEncoding]];
</pre>
Temporary queues
As you may remember previously we were using queues that had specific names (remember hello and task_queue?). Being able to name a queue was crucial for us -- we needed to point the workers to the same queue. Giving a queue a name is important when you want to share the queue between producers and consumers.
But that's not the case for our logger. We want to hear about all log messages, not just a subset of them. We're also interested only in currently flowing messages not in the old ones. To solve that we need two things.
Firstly, whenever we connect to Rabbit we need a fresh, empty queue. To do this we could create a queue with a random name, or, even better - let the library choose a random queue name for us (other clients leave this work to the server, but as the Objective-C client is designed to avoid blocking the calling thread, it prefers to generate its own names).
Secondly, once we disconnect the consumer the queue should be automatically deleted.
In the Objective-C client, when we supply queue name as an empty string, we create a non-durable queue with a generated name:
<pre style="border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px;">RMQQueue *q = [ch queue:@"" options:RMQQueueDeclareExclusive];
</pre>
When the method returns, the queue instance contains a random queue name generated by the library. For example it may look like rmq-objc-client.gen-049F8D0B-F330-4D65-9277-0F418F529A93-41604-000030FB39652E07.
When the connection that declared it closes, the queue will be deleted because it is declared as exclusive. You can learn more about the exclusive flag and other queue properties in the guide on queues.
Bindings
We've already created a fanout exchange and a queue. Now we need to tell the exchange to send messages to our queue. That relationship between exchange and a queue is called a binding.
<pre style="border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px;">[q bind:x];
</pre>
From now on the logs exchange will append messages to our queue.
Listing bindings
You can list existing bindings using, you guessed it, rabbitmqctl list_bindings.
Putting it all together
The producer method, which emits log messages, doesn't look much different from the previous tutorial. The most important change is that we now want to publish messages to our logs exchange instead of the nameless one. Here goes the code for emitLog:
<pre class="lang-objectivec hljs" style="font-style: normal; font-variant-caps: normal; text-indent: 0px; text-transform: none; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-variant-ligatures: normal; font-weight: 500; letter-spacing: 0.16px; orphans: 2; text-align: left; widows: 2; text-decoration-thickness: initial; background-position: initial initial; background-repeat: initial initial;">RMQConnection *conn = [[RMQConnection alloc] initWithDelegate:[RMQConnectionDelegateLogger new]];
[conn start];
id<RMQChannel> ch = [conn createChannel];
RMQExchange *x = [ch fanout:@"logs"];
NSString *msg = @"Hello World!";
[x publish:[msg dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(@"Sent %@", msg);
[conn close];
</pre>
As you see, after establishing the connection we declared the exchange. This step is necessary as publishing to a non-existing exchange is forbidden.
The messages will be lost if no queue is bound to the exchange yet, but that's okay for us; if no consumer is listening yet we can safely discard the message.
The code for receiveLogs:
<pre class="lang-objectivec hljs" style="font-style: normal; font-variant-caps: normal; text-indent: 0px; text-transform: none; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-variant-ligatures: normal; font-weight: 500; letter-spacing: 0.16px; orphans: 2; text-align: left; widows: 2; text-decoration-thickness: initial; background-position: initial initial; background-repeat: initial initial;">RMQConnection *conn = [[RMQConnection alloc] initWithDelegate:[RMQConnectionDelegateLogger new]];
[conn start];
id<RMQChannel> ch = [conn createChannel];
RMQExchange *x = [ch fanout:@"logs"];
RMQQueue *q = [ch queue:@"" options:RMQQueueDeclareExclusive];
[q bind:x];
NSLog(@"Waiting for logs.");
[q subscribe:^(RMQMessage * _Nonnull message) {
NSLog(@"Received %@", [[NSString alloc] initWithData:message.body encoding:NSUTF8StringEncoding]);
}];
</pre>
Using rabbitmqctl list_bindings you can verify that the code actually creates bindings and queues as we want. With two receive_logs.rb programs running you should see something like:
<pre class="lang-bash hljs" style="font-style: normal; font-variant-caps: normal; text-indent: 0px; text-transform: none; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; display: block; overflow-x: auto; padding: 0.5em; background-color: rgb(35, 35, 35); color: rgb(230, 225, 220); font-size: medium; overflow-wrap: break-word; white-space: pre-wrap; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-variant-ligatures: normal; font-weight: 500; letter-spacing: 0.16px; orphans: 2; text-align: left; widows: 2; text-decoration-thickness: initial; background-position: initial initial; background-repeat: initial initial;">sudo rabbitmqctl list_bindings
=> Listing bindings ...
=> logs exchange amq.gen-JzTY20BRgKO-HjmUJj0wLg queue []
=> logs exchange amq.gen-vso0PVvyiRIL2WoV3i48Yg queue []
=> ...done.
</pre>
The interpretation of the result is straightforward: data from exchange logs goes to two queues with generated names. And that's exactly what we intended.
To find out how to listen for a subset of messages, let's move on to tutorial 4
Production [Non-]Suitability Disclaimer
Please keep in mind that this and other tutorials are, well, tutorials. They demonstrate one new concept at a time and may intentionally oversimplify some things and leave out others. For example topics such as connection management, error handling, connection recovery, concurrency and metric collection are largely omitted for the sake of brevity. Such simplified code should not be considered production ready.
Please take a look at the rest of the documentation before going live with your app. We particularly recommend the following guides: Publisher Confirms and Consumer Acknowledgements, Production Checklist and Monitoring.
Getting Help and Providing Feedback
If you have questions about the contents of this tutorial or any other topic related to RabbitMQ, don't hesitate to ask them on the RabbitMQ mailing list.
Help Us Improve the Docs <3
If you'd like to contribute an improvement to the site, its source is available on GitHub. Simply fork the repository and submit a pull request. Thank you!
注:以上为网址上摘抄