On 6 March 2015 at 01:27:02, Scott Nichol (
charlessc...@gmail.com) wrote:
> In order to eliminate as many red herrings as possible, this
> RabbitMQ instance is in a dev environment being accessed by only
> a few applications. Of particular interest regarding flow,
> there is one connection with a single channel that publishes
> to a topic exchange named exchange.publish. The messages published
> to exchange.publish are routed to a durable queue named jenga.danube.
> There is another connection with eight channels, where each
> channel has a consumer for jenga.danube with a prefetch of 1000.
> (Note that I have tried many different prefetch values, none
> of which avoids flow altogether.)
>
> The message publisher is very bursty. The consumers are slow
> relative to the bursts, but over time windows of hours the queues
> always clear. We see the flow scenario on both Windows and Linux
> and on both modest and quite overpowered (24 cores with SSDs)
> machines. I never see indications in tools like iostat, vmstat
> or perfmon that the machines are stressed.
>
> I can provide many more details, but none are entirely germane
> to my core question for this post, which is whether there is anything
> in the report output that indicates why the publishing connection
> is in flow?
Developers tend to see systems (e.g. a running RabbitMQ node) as mostly constant:
if you continuously feed it inputs of X, you should get Ys pretty much always.
Unfortunately, real world systems (software, hardware, anything) are rarely
perfectly stable. There's quite a bit going on even on an idle machine. Disk performance
is not constant, CPU performance is not constant (modern CPUs almost always have
bursting which they employ for periods of time as they see fit), network throughput
is not constant. You may have "busy neighbours" in your environment
without realising it (certainly very common in public cloud environments but also
in private ones when it's not uncommon for VMs to share a NAS device, for instance).
On top of that, the OS kernel and runtimes (the Erlang VM in our case) employ sophisticated
algorithms in their schedulers that are not necessarily easy to predict. They can do things
differently this minute compared to the previous one depending on many factors.
As the result, throughput of your system *will* vary over time, even if the fluctuations are small
and sometimes very hard to observe even with solid monitoring in place.
Given all this, why do you see channels go into flow control for brief periods of time?
RabbitMQ is built with Erlang and uses a bunch of processes for all kinds of things internally.
Channels are represented as processes, for example. So are queues.
When [AMQP 0-9-1/STOMP/MQTT/etc] messages come in, they are parsed and passed along as Erlang
messages between processes. Processes have mailboxes — queues (as in data structures, not RabbitMQ
queues) that grow if more messages come in on average than is processed. This means that if
process A cannot keep up with process B and B continues sending it messages, A's mailbox will
keep growing, consuming more and more RAM. To guard against this, RabbitMQ has a small
utility module — credit_flow — that allows processes to control how many messages they are
allowed to send according to the receiving end.
If your channel runs into flow control, it means it tries to go faster than some other part
of the node, e.g. a queue or channel writer (a helper process that serialises protocol methods and
sends resulting data to the socket). This can happen for a bunch of reasons:
* Some processes do a lot more work than others
* Some processes (queues) have to do disk or network I/O, which is expensive relative to in-memory operations
* The runtime has prioritised scheduling some processes over others
* I/O operations temporarily run slower (e.g. a busy neighbour has loaded the network)
* Clients that consume messages became slower
* RabbitMQ decided to move a lot of messages to or from disk , e.g. as more or less RAM became available
and so on. Some part of the system temporarily becomes a bottleneck. Some are more likely to become
the bottleneck than others:
* Those that do I/O, in particular disk I/O
* Those that may transfer messages in bulk
* Some processes, in particular channels, are naturally hot spots because so much goes through them
> I'm looking for something concrete that I might be
> able to act on.
You need to first understand what part of the node slows down. Use tools such as iostat, for example.
Collect CPU usage metrics. Once you have a decent understanding of what it is, you'll probably know
how to act on that. RabbitMQ 3.5.0 will display some I/O statistics right in the management UI. Traffic
to and from client on a connection is displayed in any reasonably recent release.
Sorry, there is little room for generic advice when it comes to tuning for throughput. Only measurements
can tell what exactly is the bottleneck.
Be prepared that the reason may be less than obvious. Erlang VM generally does a great job of scheduling
things under load but it cannot cover every workload equally well (more on this in [1][4]).
It has quite a few flags to tune [2]. Finding the right balance with them requires a lot of experimentation.
It may be easier and/or cheaper to just "throw more hardware at it" in some cases.
Before you blame the VM, keep in mind that it has its sophisticated scheduler for a reason:
the infamous GC pause problem pretty much does not exist in Erlang (certain language aspects
helped make this possible). So it may take a very smart person to outsmart it.
Another thing we mention over and over: eliminating flow control is trading [CPU] time
for space (RAM). Once you go above a particular threshold, you either hit resource
flow control or your process gets killed by the OOM killer (or similar). Pyrrhic victory.
Alternatively you
can drop some data on the floor — that is not a particularly popular option but future
RabbitMQ versions may include featured that would allow you to make the queue delete
messages instead of paging them to disk under memory pressure (as an example).
1.
http://jlouisramblings.blogspot.ru/2013/01/how-erlang-does-scheduling.html
2.
http://docs.basho.com/riak/2.0.4/ops/tuning/erlang/
3.
http://erlang.org/doc/man/erl.html
4.
https://www.erlang-solutions.com/resources/webinars/understanding-erlang-scheduler
--
MK
Staff Software Engineer, Pivotal/RabbitMQ