用Golang创建一个RabbitMQ生产者实例

192 阅读6分钟

在这个例子中,我们将创建一个 RabbitMQ 生产者来在 Golang 中发布消息。我们将只是发布消息,所以这里没有消费者的参与。在继续之前,让我把一些事情说清楚。发布/订阅和生产者/消费者这两个术语都与消息传递有关,但它们是不同的东西。发布/订阅是一种消息传递模式,由生产者/消费者执行。生产者发布消息,消费者订阅消息。它们都不属于 RabbitMQ。它们是您的应用程序的一部分。只有交换、路由/绑定键和队列是 RabbitMQ 的一部分。

它是做什么的?

这里列出的每一点都在下面的 "注意事项 "部分有相关信息。

  • 连接被命名,以便在 RabbitMQ UI 中更容易识别哪个连接属于哪个应用程序。

  • 在应用程序启动时仅创建一个长期存在的 AMQP 连接,供所有 API 请求/线程使用。

  • 为要使用的每个 API 请求/线程创建专用通道。

  • 队列被声明为 "懒惰的",以防止内存在大流量下被敲打,还有更多。

  • 如果连接意外中断,一旦发生第一次发布,就会通过使用Connection.NotifyClose 监听器(由RabbitMQ.reconnect() 方法处理)或RabbitMQ.Channel() 方法自动创建一个新的连接。然而,如果重新连接过程持续失败,它在第6次尝试后就放弃了。如果连接在应用程序本身中出现了预期的故障,那么什么也不会做。

  • 如果消息没有被传递到交易所或队列中,它会返回500 状态代码。虽然这不太可能,但在默认情况下,如果交付确认超时,它将返回202 状态代码。确认超时被设置为100 millisecond。这不一定是一个错误。它只是告诉您,RabbitMQ 没有设法在一定的时间范围内确认消息的交付。它发生在系统处于非常高的流量下时。

注释

连接(数据竞赛)

尽管不太可能,但只有在同时满足以下两种情况时,RabbitMQ.reconnect() 方法才可能(并非总是)在终端中转出数据竞赛警告。

  • 连接在非常短的时间内被连续关闭(非常不正常)。

  • 应用程序在非常短的时间内连续收到请求(正常)。

然而,如果你只面对第一种情况,你实际上可能有某种架构/网络/服务器层面的问题,而不是应用程序层面的问题,除非你真的把你的应用程序编程得很糟糕这个例子中的设置根本就不应该导致第一种情况的发生。即使您在 RabbitMQ UI 中手动关闭连接,应用程序也会为您建立一个新连接。

通道(内存)

我正在为每个请求/线程提供一个孤立的通道,以防止RabbitMQ.reconnect() 方法中可能出现的数据竞赛问题。然而,这导致了更高的通道流失率(我强烈建议你阅读这一页),但至少它可以防止应用程序宕机,所以你必须以某种方式做出牺牲。此外,每个线程打开和关闭通道既不罕见也不可怕。RabbitMQ 文档中写道*:"对于使用多个线程/进程进行处理的应用程序,为每个线程/进程打开一个新通道并且不在它们之间共享通道是非常常见的",这正是该应用程序正在做的事情。随之而来的是,它使用的内存量真的很小。这一类是所有通道进程在任何时候使用的内存之和。作为证明,如果你在 RabbitMQ 服务器中运行rabbitmq-diagnostics memory_breakdown -q --unit mb 命令,你会看到如下内容。connection_channels 的官方描述是"客户端连接使用的通道越多,该类别使用的内存就越多"*。有关内存使用的更多信息,请阅读《关于内存使用的推理》

allocated_unused: 31.6314 mb (23.3%)

队列(内存)

我们使用Lazy Queues来避免内存过载。懒惰队列尽可能早地将消息移到磁盘上,只有在消费者要求时才将其加载到RAM中。这个功能有很多好处,所以请阅读文档以了解更多信息。

资源使用情况(测试)

假设你连续发送1000个请求。当这样做的时候,每隔5个请求就会故意关闭连接。在这种情况下,你的应用程序将带来最多2个额外的goroutine(临时),并消耗0.2到2MB的内存,这对系统资源来说是非常轻的。如果你同时发送1000个连续的请求,只有内存使用量上升到3MB,这仍然不算多。记住,这个用量不会是恒定的,所以有时会更少。我在2000个连续请求中几乎没有经历过3MB。

上面的结果与应用程序有关。现在让我们来看看RabbitMQ是如何表现的。我有 100 个用户在 10 秒内连续发送并发请求。RabbitMQ 通道的平均使用量为 1.5MB,最大约为 4MB。仍然非常高效。我强烈建议在 RabbitMQ 服务器中运行rabbitmq-plugins enable rabbitmq_top 命令以启用rabbitmq-top插件,该插件有助于分析消耗最多内存或 CPU 时间的运行时进程。这将在 "管理 "标签下添加 "顶级进程 "菜单项。

重新连接

如果您转到 RabbitMQ UI 并关闭连接以模拟网络错误,您将看到您的应用程序重新连接。日志应该看起来像下面这样。

2020/05/05 20:48:35 CRITICAL: Connection dropped, reconnecting

如果您在您的应用程序中手动关闭它以模拟正常操作,您将看到您的应用程序不会重新连接。日志应该如下所示。

2020/05/05 20:46:34 INFO: Connection dropped normally, will not reconnect

消息传递确认测试

这一部分向你展示了Channel.NotifyPublishChannel.NotifyReturn 如何处理消息发布。您只想在向 RabbitMQ 发布消息后向用户返回一个正确的响应。没有监听器的发布功能并不一定意味着消息已被交付。因此,我们需要等待确认的原因。

确认代码

...

测试案例

下面的列表显示了哪些监听器/处理程序捕获了哪些情况。

# EXCHANGE QUEUE CONSUMER RESPONSE DELAY HANDLER       ERROR

如果你为两个监听器转储ntf 变量,你会得到以下结果。

# 1

RabbiqMQ服务器

如果你愿意,你可以使用下面的编译文件,并用docker-compose up 命令来运行它。然后你可以通过http://0.0.0.0:15672/ 地址访问用户界面,用guest:guest 凭证登录。

version: "3.4"

应用程序

我们现在转向应用程序。首先使用go get github.com/streadway/amqp 来安装 RabbitMQ 客户端。我们的示例使用以下属性。

ExchangeType: direct

我们有一个端点http://localhost:8080/users/create ,它将被使用。如果你愿意,你可以添加更多的端点和它们自己的 RabbitMQ 属性,与上面类似。

结构

├── cmd

文件

main.go

package main

app.go

package app

config.go

package config

router.go

package http

服务器.go

package http

rabbitmq.go

package rabbitmq

amqp.go

package user

创建.go

package user

例子