天天看點

dissecting-disruptor-wiring-up

logically the next thing to do is to wire everything up together.

i talked about multiple producers – they have the producer barrier to

keep them in order and under control.  i’ve talked about consumers in a

some clever stuff to allow the consumers to be dependent on each other

and the ring buffer.  like a lot of applications, we have a pipeline of

things that need to happen before we can actually get on with the

business logic – for example, we need to make sure the messages have

been journalled to disk before we can do anything.

the performance tests cover some basic configurations that you might

want. i’m going to go over the most interesting one, mostly because i

needed the practice with the graphics tablet.

<b>diamond configuration</b>

a configuration which is not too uncommon – a single producer with

three consumers.  the tricky point being that the third consumer is

dependent upon the previous two consumers to finish before it can do

anything.

dissecting-disruptor-wiring-up

consumer three might be your business logic, consumer one could be

backing up the data received, and consumer two may be preparing the data

or something.

<b>diamond configuration using queues</b>

dissecting-disruptor-wiring-up

(why does queue have to have so many “e”s?  it’s the letter i have the most trouble with in these drawings).

you might get an inkling of the problem here: for a message to get

from p1 to c3 it has to travel through four whole queues, each queue

taking its cost in terms of putting the message on the queue and taking

it off again.

<b>diamond configuration using the disruptor</b>

dissecting-disruptor-wiring-up

it does look more complicated.  but the ring buffer remains the

single point of contact between all the players, and the interactions

are all based on the barriers checking the sequence numbers of the

things it’s dependent upon.

interestingly, the producer barrier doesn’t have to care about all the

consumers.  it only cares about consumer three, because if consumer

three has finished with an item in the ring buffer the other two will

already have processed it.  so if c3 has moved on, that slot in the ring

buffer is available.

to manage the dependencies between the consumers you need two

consumer barriers.  the first just talks to the ring buffer and

consumers one and two ask it for the next available item.  the second

consumer barrier knows about consumers one and two, and it will return

the lowest sequence number processed by both consumers.

<b>how consumer dependencies work in the disruptor</b>

hmm.  i can see i’m going to need an example.

dissecting-disruptor-wiring-up

we’re joining the party halfway through the story: the producer has

filled the ring buffer up to sequence number 22; consumer one has read

and processed everything up to 21; consumer two has processed everything

up to sequence 18; consumer three, which is dependent upon the other

consumers, has only made it as far as 15.

the producer can’t write anything more to the ring buffer because

sequence 15 is taking up the slot where we’d want to put sequence 23.

dissecting-disruptor-wiring-up

(i’m sorry, i really did try to find an alternative to red and green, but everything else was just as ambiguous).

the first consumer barrier lets consumers one and two know they can

grab anything up to sequence 22, the highest sequence number in the ring

buffer.  the second consumer barrier checks the ring buffer sequence,

but it also checks the sequences on the other two consumers and returns

the lowest value.  so consumer three is told it can get anything up to

sequence 18 from the ring buffer.

note that the consumers are still reading the entries directly from

the ring buffer – consumers one and two are not taking the entries off

the ring buffer and then passing them on to consumer three.  instead,

the second consumer barrier is letting consumer three know which entry

in the ring buffer it’s safe to process.

this raises a question – if everything comes directly off the ring

buffer, how is consumer three going to find out about anything the first

two consumers have done?  if all consumer three cares about is that the

earlier consumers have done their job (e.g. replicating the data to

somewhere else) then everything’s fine – when consumer three is told the

job is done, it’s happy.  if, however, consumer three needs the results

of an earlier consumer’s processing, where does it get that from?

<b>modifying entries</b>

the secret is to write them to the ring buffer <code>entry</code> itself.  this way, when consumer three grabs the entry off the ring buffer, it will have

been populated with all the information consumer three needs to do

the job.  the really important part of this is that for each field on

the <code>entry</code> only one consumer is allowed to write to it.  this prevents any write-contention which will slow the whole thing down.

dissecting-disruptor-wiring-up

 the third consumer, fizzbuzz, will read both of these fields but not

write to either, since reading is fine and won’t cause contention.

<b>some actual java code</b>

all this looks more complicated than the queue implementation.  and yes,

it does involve a bit more coordination.  but this is hidden from the

consumers and producers, they just talk to the barriers.  the trick is

in the configuration.  the diamond graph in the example above would be

created using something like the following:

<b>in summary</b>

so there you have it – how to wire up the disruptor with multiple consumers that are dependent on each other.  the key points:

use multiple consumer barriers to manage dependencies between consumers.

have the producer barrier watch the last consumer in the graph.

allow only one consumer to write to an individual field in an <code>entry</code>.