Networking and RabbitMQ
Overview
Clients communicate with RabbitMQ over the network. All protocols supported by the broker are TCP-based. Both RabbitMQ and the operating system provide a number of knobs that can be tweaked. Some of them are directly related to TCP and IP operations, others have to do with application-level protocols such as TLS. This guide covers multiple topics related to networking in the context of RabbitMQ. This guide is not meant to be an extensive reference but rather an overview. Some tuneable parameters discussed are OS-specific. This guide focuses on Linux when covering OS-specific subjects, as it is the most common platform RabbitMQ is deployed on.
There are several areas which can be configured or tuned. Each has a section in this guide:
- Interfaces the node listens on for client connections
- IP version preferences: dual stack, IPv6-only and IPv4-only
- Ports used by clients, inter-node traffic in clusters and CLI tools
- IPv6 support for inter-node traffic
- TLS for client connections
- Tuning for a large number of concurrent connections
- High connection churn scenarios and resource exhaustion
- TCP buffer size (affects throughput and how much memory is used per connection)
- Hostname resolution-related topics such as reverse DNS lookups
- The interface and port used by epmd
- How to suspend and resume listeners to temporarily stop and resume new client connections
- Other TCP socket settings
- Proxy protocol support for client connections
- Kernel TCP settings and limits (e.g. TCP keepalives and open file handle limit)
- How to allow Erlang runtime to accept inbound connections when MacOS Application Firewall is enabled
- OS-level tuning related to networking
This guide also covers a few topics closely related to networking:
Except for OS kernel parameters and DNS, all RabbitMQ settings are configured via RabbitMQ configuration file(s).
Networking is a broad topic. There are many configuration options that can have positive or negative effect on certain workloads. As such, this guide does not try to be a complete reference but rather offer an index of key tunable parameters and serve as a starting point.
In addition, this guide touches on a few topics closely related to networking, such as
- Hostnames, hostname resolution and DNS
- connection lifecycle logging
- Heartbeats (a.k.a. keepalives)
- proxies and load balancers
VMware Tanzu RabbitMQ commercial offerings provide an Intra-cluster Compression feature. The previous documentation link goes to the Tanzu RabbitMQ for Kubernetes commercial offering.
A methodology for troubleshooting of networking-related issues is covered in a separate guide.
Network Interfaces for Client Connections
For RabbitMQ to accept client connections, it needs to bind to one or more
interfaces and listen on (protocol-specific) ports. One such interface/port pair is called a listener
in RabbitMQ parlance. Listeners are configured using the listeners.tcp.*
configuration option(s).
TCP listeners configure both an interface and port. The following example demonstrates how to configure AMQP 0-9-1 and AMQP 1.0 listener to use a specific IP and the standard port:
listeners.tcp.1 = 192.168.1.99:5672
By default, RabbitMQ will listen on port 5672 on all available interfaces. It is possible to limit client connections to a subset of the interfaces or even just one, for example, IPv6-only interfaces. The following few sections demonstrate how to do it.
Listening on Dual Stack (Both IPv4 and IPv6) Interfaces
The following example demonstrates how to configure RabbitMQ to listen on localhost only for both IPv4 and IPv6:
listeners.tcp.1 = 127.0.0.1:5672
listeners.tcp.2 = ::1:5672
With modern Linux kernels and Windows releases, when a port is specified and RabbitMQ is configured to listen on all IPv6 addresses but IPv4 is not deactivated explicitly, IPv4 address will be included, so
listeners.tcp.1 = :::5672
is equivalent to
listeners.tcp.1 = 0.0.0.0:5672
listeners.tcp.2 = :::5672
Listening on IPv6 Interfaces Only
In this example RabbitMQ will listen on an IPv6 interface only:
listeners.tcp.1 = fe80::2acf:e9ff:fe17:f97b:5672
In IPv6-only environments the node must also be configured to use IPv6 for inter-node communication and CLI tool connections.
Listening on IPv4 Interfaces Only
In this example RabbitMQ will listen on an IPv4 interface with specified IP address only:
listeners.tcp.1 = 192.168.1.99:5672 # Plain AMQP
listeners.ssl.1 = 192.168.1.99:5671 # TLS (AMQPS)
It is possible to deactivate non-TLS connections by deactivating all regular TCP listeners. Only TLS-enabled clients will be able to connect:
# deactivates non-TLS listeners, only TLS-enabled (activated) clients will be able to connect
listeners.tcp = none
listeners.ssl.default = 5671
ssl_options.cacertfile = /path/to/ca_certificate.pem
ssl_options.certfile = /path/to/server_certificate.pem
ssl_options.keyfile = /path/to/server_key.pem
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = false
Port Access
RabbitMQ nodes bind to ports (open server TCP sockets) in order to accept client and CLI tool connections. Other processes and tools such as SELinux may prevent RabbitMQ from binding to a port. When that happens, the node will fail to start.
CLI tools, client libraries and RabbitMQ nodes also open connections (client TCP sockets). Firewalls can prevent nodes and CLI tools from communicating with each other. Make sure the following ports are accessible:
- 4369: epmd, a peer discovery service used by RabbitMQ nodes and CLI tools
- 5672, 5671: used by AMQP 0-9-1 and AMQP 1.0 clients without and with TLS
- 5552, 5551: used by the RabbitMQ Stream protocol clients without and with TLS
- 6000 through 6500: used for stream replication
- 25672: used for inter-node and CLI tools communication (Erlang distribution server port) and is allocated from a dynamic range (limited to a single port by default, computed as AMQP port + 20000). Unless external connections on these ports are really necessary (e.g. the cluster uses federation or CLI tools are used on machines outside the subnet), these ports should not be publicly exposed
- 35672-35682: this client TCP port range is used by CLI tools for communication with nodes.
By default, the range computed as
(server distribution port + 10000)
through(server distribution port + 10010)
- 15672, 15671: HTTP API clients, management UI and rabbitmqadmin, without and with TLS (only if the management plugin is enabled)
- 61613, 61614: STOMP clients without and with TLS (only if the STOMP plugin is enabled)
- 1883, 8883: MQTT clients without and with TLS, if the MQTT plugin is enabled
- 15674: STOMP-over-WebSockets clients (only if the Web STOMP plugin is enabled)
- 15675: MQTT-over-WebSockets clients (only if the Web MQTT plugin is enabled)
- 15692, 15691: Prometheus metrics, without and with TLS (only if the Prometheus plugin is enabled)
It is possible to configure RabbitMQ to use different ports and specific network interfaces.
How to Temporarily Stop New Client Connections
Client connection listeners can be suspended to prevent new client connections from being accepted. Existing connections will not be affected in any way.
This can be useful during node operations and is one of the steps performed when a node is put into maintenance mode.
To suspend all listeners on a node and prevent new client connections to it, use rabbitmqctl suspend_listeners
:
rabbitmqctl suspend_listeners
As all other CLI commands, this command can be invoked against an arbitrary node (including remote ones)
using the -n
switch:
# suspends listeners on node rabbit@node2.cluster.rabbitmq.svc: it won't accept any new client connections
rabbitmqctl suspend_listeners -n rabbit@node2.cluster.rabbitmq.svc
To resume all listeners on a node and make it accept new client connections again, use rabbitmqctl resume_listeners
:
rabbitmqctl resume_listeners
# resumes listeners on node rabbit@node2.cluster.rabbitmq.svc: it will accept new client connections again
rabbitmqctl resume_listeners -n rabbit@node2.cluster.rabbitmq.svc
Both operations will leave log entries in the node's log.
EPMD and Inter-node Communication
What is EPMD and How is It Used?
epmd (for Erlang Port Mapping Daemon) is a small additional daemon that runs alongside every RabbitMQ node and is used by the runtime to discover what port a particular node listens on for inter-node communication. The port is then used by peer nodes and CLI tools.
When a node or CLI tool needs to contact node rabbit@hostname2
it will do the following:
- Resolve
hostname2
to an IPv4 or IPv6 address using the standard OS resolver or a custom one specified in the inetrc file - Contact
epmd
running onhostname2
using the above address - Ask
epmd
for the port used by noderabbit
on it - Connect to the node using the resolved IP address and the discovered port
- Proceed with communication
EPMD Interface
epmd
will listen on all interfaces by default. It can
be limited to a number of interfaces using the ERL_EPMD_ADDRESS
environment variable:
# makes epmd listen on loopback IPv6 and IPv4 interfaces
export ERL_EPMD_ADDRESS="::1"
When ERL_EPMD_ADDRESS
is changed, both RabbitMQ node and epmd
on the host must be stopped.
For epmd
, use
# Stops local epmd process.
# Use after shutting down RabbitMQ.
epmd -kill
to terminate it. The service will be started by the local RabbitMQ node automatically on boot.
The loopback interface will be implicitly added
to that list (in other words, epmd
will always bind to the loopback interface).
EPMD Port
The default epmd port is 4369, but this can be changed using the ERL_EPMD_PORT
environment
variable:
# makes epmd bind to port 4369
export ERL_EPMD_PORT="4369"
All hosts in a cluster must use the same port.
When ERL_EPMD_PORT
is changed, both RabbitMQ node and epmd
on the host must be stopped.
For epmd
, use
# Stops local epmd process.
# Use after shutting down RabbitMQ.
epmd -kill
to terminate it. The service will be started by the local RabbitMQ node automatically on boot.
Inter-node Communication Port Range
RabbitMQ nodes will use a port from a certain range known as the inter-node communication port range. The same port is used by CLI tools when they need to contact the node. The range can be modified.
RabbitMQ nodes communicate with CLI tools and other nodes using a port known as
the distribution port. It is dynamically allocated from a range of values.
For RabbitMQ, the default range is limited to a single value computed as
RABBITMQ_NODE_PORT
(AMQP 0-9-1 and AMQP 1.0 port) + 20000, which results
in using port 25672. This single port can be configured
using the RABBITMQ_DIST_PORT
environment variable.
When configuring firewall rules, remote connections on the inter-node communication port must be allowed from every cluster node's IP address and every host where CLI tools might be used
RabbitMQ command line tools also use a range of ports. The default range is computed by taking the RabbitMQ
distribution port value and adding 10000 to it. The next 10 ports are also part
of this range. Thus, by default, this range is 35672 through 35682. This range
can be configured using the RABBITMQ_CTL_DIST_PORT_MIN
and RABBITMQ_CTL_DIST_PORT_MAX
environment variables.
Note that limiting the range to a single port will prevent more than one CLI
tool from running concurrently on the same host and may affect CLI commands
that require parallel connections to multiple cluster nodes. A port range of 10
is therefore a recommended value.
When configuring firewall rules, remote connections on the inter-node communication port must be allowed from every cluster node's IP address and every host where CLI tools might be used. epmd port must be open for CLI tools and clustering to function.
On Windows, the following settings have no effect when RabbitMQ runs as a service. Please see Windows Configuration for details.
The range used by RabbitMQ can also be controlled via two configuration keys
in rabbitmq.conf
:
inet_dist_listen_min
inet_dist_listen_max
They define the range's lower and upper bounds, inclusive.
The example below uses a range with a single port but a value different from default:
inet_dist_listen_min = 33672
inet_dist_listen_max = 33672
To verify what port is used by a node for inter-node and CLI tool communication, run
epmd -names
on that node's host. It will produce output that looks like this:
epmd: up and running on port 4369 with data:
name rabbit at port 25672
Inter-node Communication Buffer Size Limit
Inter-node connections use a buffer for data pending to be sent. Temporary
throttling on inter-node traffic is applied when the buffer is at max allowed
capacity. The limit is controlled via the RABBITMQ_DISTRIBUTION_BUFFER_SIZE
environment variable
in kilobytes. Default value is 128 MB (128000
kB).
In clusters with heavy inter-node traffic increasing this value may have a positive effect on throughput. Values lower than 64 MB are not recommended.
Using IPv6 for Inter-node Communication (and CLI Tools)
In addition to exclusive IPv6 use for client connections for client connections, a node can also be configured to use IPv6 exclusively for inter-node and CLI tool connectivity.
This involves configuration in a few places:
- Inter-node communication protocol setting in the runtime
- Configuring IPv6 to be used by CLI tools
- epmd, a service involved in inter-node communication (discovery)
It is possible to use IPv6 for inter-node and CLI tool communication but use IPv4 for client connections or vice versa. Such configurations can be hard to troubleshoot and reason about, so using the same IP version (e.g. IPv6) across the board or a dual stack setup is recommended.
Inter-node Communication Protocol
To instruct the runtime to use IPv6 for inter-node communication and related tasks, use
the RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS
environment variable to pass a couple of flags:
# these flags will be used by RabbitMQ nodes
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="-kernel inetrc '/etc/rabbitmq/erl_inetrc' -proto_dist inet6_tcp"
# these flags will be used by CLI tools
RABBITMQ_CTL_ERL_ARGS="-proto_dist inet6_tcp"
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS
above uses two closely related flags:
-kernel inetrc
to configure a path to an inetrc file that controls hostname resolution-proto_dist inet6_tcp
to tell the node to use IPv6 when connecting to peer nodes and listening for CLI tool connections
The erl_inetrc
file at /etc/rabbitmq/erl_inetrc
will control hostname resolution settings.
For IPv6-only environments, it must include the following line:
%% Tells DNS client on RabbitMQ nodes and CLI tools to resolve hostnames to IPv6 addresses.
%% The trailing dot is not optional.
{inet6,true}.
CLI Tools
With CLI tools, use the same runtime flag as used for RabbitMQ nodes above but provide it
using a different environment variable, RABBITMQ_CTL_ERL_ARGS
:
RABBITMQ_CTL_ERL_ARGS="-proto_dist inet6_tcp"
Note that once instructed to use IPv6, CLI tools won't be able to connect to nodes that
do not use IPv6 for inter-node communication. This involves the epmd
service running on the same
host as target RabbitMQ node.
epmd
epmd
is a small helper daemon that runs next to a RabbitMQ node and lets its peers and CLI
tools discover what port they should use to communicate to it. It can be configured to bind
to a specific interface, much like RabbitMQ listeners. This is done using the ERL_EPMD_ADDRESS
environment variable:
export ERL_EPMD_ADDRESS="::1"
By default RabbitMQ nodes will use an IPv4 interface when connecting to epmd
.
Nodes that are configured to use IPv6 for inter-node communication (see above)
will also use IPv6 to connect to epmd
.
When epmd
is configured to use IPv6 exclusively but RabbitMQ nodes are not,
RabbitMQ will log an error message similar to this:
Protocol 'inet_tcp': register/listen error: econnrefused
systemd Unit File
On distributions that use systemd, the epmd.socket
service controls network settings of epmd
.
It is possible to configure epmd
to only listen on IPv6 interfaces:
ListenStream=[::1]:4369
The service will need reloading after its unit file has been updated:
systemctl daemon-reload
systemctl restart epmd.socket epmd.service
Intermediaries: Proxies and Load Balancers
Proxies and load balancers are fairly commonly used to distribute client connections between cluster nodes. Proxies can also be useful to make it possible for clients to access RabbitMQ nodes without exposing them publicly. Intermediaries can also have side effects on connections.
Proxy Effects
Proxies and load balancers introduce an extra network hop (or even multiple ones) between client and its target node. Intermediaries also can become a network contention point: their throughput will then become a limiting factor for the entire system. Network bandwidth overprovisioning and throughput monitoring for proxies and load balancers are therefore very important.
Intermediaries also may terminate "idle" TCP connections when there's no activity on them for a certain period of time. Most of the time it is not desirable. Such events will result in abrupt connection closure log messages on the server end and I/O exceptions on the client end.
When heartbeats are enabled on a connection, it results in periodic light network traffic. Therefore heartbeats have a side effect of guarding client connections that can go idle for periods of time against premature closure by proxies and load balancers.
Heartbeat timeouts from 10 to 30 seconds will produce periodic network traffic often enough (roughly every 5 to 15 seconds) to satisfy defaults of most proxy tools and load balancers. Values that are too low will produce false positives.
Proxy Protocol
RabbitMQ supports Proxy protocol versions 1 (text header format) and 2 (binary header format).
The protocol makes servers such as RabbitMQ aware of the actual client IP address when connections go over a proxy (e.g. HAproxy or AWS ELB). This makes it easier for the operator to inspect connection origins in the management UI or CLI tools.
The protocol spec dictates that either it must be applied to all connections or none of them for security reasons, this feature is turned off by default and needs to be turned on for individual protocols supported by RabbitMQ. To turn it on for AMQP 0-9-1 and AMQP 1.0 clients:
proxy_protocol = true
When proxy protocol is turned on, clients won't be able to connect to RabbitMQ directly unless they themselves support the protocol. Therefore, when this option is turned on, all client connections must go through a proxy that also supports the protocol and is configured to send a Proxy protocol header. HAproxy and AWS ELB documentation explains how to do it.
When proxy protocol is turned on and connections go through a compatible proxy, no action or modifications are required from client libraries. The communication is entirely transparent to them.
STOMP and MQTT, as well as Web STOMP and Web MQTT have their own settings that enable support for the proxy protocol.
TLS (SSL) Support
It is possible to encrypt connections using TLS with RabbitMQ. Authentication using peer certificates is also possible. Please refer to the TLS/SSL guide for more information.
Tuning for Throughput
Tuning for throughput is a common goal. Improvements can be achieved by
- Increasing TCP buffer sizes
- Ensuring Nagle's algorithm is turned off
- Turning on optional TCP features and extensions
For the latter two, see the OS-level tuning section below.
Note that tuning for throughput will involve trade-offs. For example, increasing TCP buffer sizes will increase the amount of RAM used by every connection, which can be a significant total server RAM use increase.