This is a part of Disruptor's comprehensive analysis of end-to-end view. Be careful. This article is very long. But I decided to write them into a blog so that you can contact the context to read them.
TheKey PointsYes: do not overlap the Ring; notify consumers; batch processing at the producer end; and how multiple producers work together.
ProducerBarriers
Disruptor codeConsumerProvides some interfaces and auxiliary classes, but does not write the Ring BufferProducerProvides interfaces. This is because no one except you need to know the producer needs to access it. Even so, the Ring Buffer provides a ProducerBarrier object like the consumer, allowing the producer to write the Ring Buffer through it.
Writing a Ring Buffer involves two-phase commit (two-phase commit ). First, your producer needs to apply for the next node in the buffer. Then, when the producer writes data to the node, it will call the commit method of ProducerBarrier.
Let's take a look at the first step. "Give me the next node in the Ring Buffer". This sentence sounds very simple. Indeed, from the producer's perspective, it is very simple: Simply calling the nextEntry () method of ProducerBarrier will return you an Entry object, which is the next node of the Ring Buffer.
How does ProducerBarrier prevent Ring Buffer overlap?
In the background, ProducerBarrier is responsible for all interaction details to find the next node from the Ring Buffer before the producer can write data to it.
I'm not sure whether the shiny new tablet can help improve the definition of my picture, but it's interesting to use ).
In this figure, we assume that only one producer writes the Ring Buffer. After a while, we will deal with the complex problems of multiple producers.
ConsumerTrackingProducerBarrierThe object has allConsumerColumn table. This looks a bit strange-I never expected ProducerBarrier to know anything about the consumer side. But wait, there is a reason. Because we don't want to "confuse" the queue with the queue and we need to track the header and tail of the queue. They sometimes point to the same bit.) The Disruptor is responsible for notifying the consumer of the serial number they have processed, rather than Ring Buffer. Therefore, if we want to determine that we have not overlapped the Ring Buffer, we need to check where all consumers have read it.
In, there isConsumerSuccessfully read the maximum number 12 highlighted in red/pink ). SecondConsumerA little backward -- maybe it is doing I/O operations and so on -- it stops at number 3. Therefore, before catching up with consumer 1, consumer 2 must finish the whole Ring Buffer circle.
Now the producer wants to write the node occupied by serial number 3 in the Ring Buffer because it is the next node of the current cursor of the Ring Buffer. However, ProducerBarrier understands that data cannot be written because a consumer is occupying it. Therefore, ProducerBarrier stops to spin (spins) and waits until the consumer leaves.
Apply for the next node
Now we can imagine that consumer 2 has processed a batch of nodes and moved its serial number forward. It may be moved to number 9 because of the batch processing method on the consumer end. In reality, I will expect it to reach 12, but this example is not interesting enough ).
Displays the situation when consumer 2 moves to number 9. In this figure, I have ignored ConsumerBarrier because it is not involved in this scenario.
ProducerBarier will see that the next node -- number 3 is ready for use. It will seize the Entry object on this node. I have not specifically introduced the Entry object. Basically, it is a bucket for storing the Ring Buffer data written to a sequence number) update the Entry number and return the Entry to the producer. The producer can then write data to the Entry.
Submit new data
The second step of the two-phase commit is-yes, commit.
Green indicates the recently written Entry. The serial number is 13 -- er. Sorry, I am also red-green blind. But other colors are even worse.
When the producer ends writing data to the Entry, it will require the ProducerBarrier to submit.
ProducerBarrier waits for the cursor of the Ring Buffer to catch up with the current position, which is meaningless for a single producer-for example, we already know that the cursor has reached 12, and no one else is writing the Ring Buffer ). Then ProducerBarrier updates the cursor of the Ring Buffer to the Entry number written just now-here we are 13. Next, ProducerBarrier will let consumers know that there are new things in the buffer. It stamps the WaitStrategy object on the ConsumerBarrier and says, "Hey, wake up! Something happened !" Note-different WaitStrategy implementations implement reminders in different ways, depending on whether it uses the blocking mode .)
Now consumer 1 can read the data of Entry 13, consumer 2 can read Entry 13 and all the data above, and then they are all very happy.
Batch Processing on ProducerBarrier
Interestingly, Disruptor can implement batch processing at both the producer and consumer ends. Do you still remember that consumer 2 finally reached the number 9 with the program running? ProducerBarrier can do something tricky here-It knows the size of the Ring Buffer and the slowest consumer location. Therefore, it can discover which nodes are currently available.
If ProducerBarrier knows that the cursor of the Ring Buffer points to 12, and the slowest consumer is at 9, it allows the producer to write nodes 3, 4, 5, 6, 7, and 8, you do not need to check the consumer location again in the middle.
Scenarios with multiple producers
Here you may think that I have finished speaking, but there are still some details.
In the above figure, I lied a little bit. I hinted that the sequence number obtained by ProducerBarrier is directly from the cursor of the Ring Buffer. However, if you have read the code, you will find that it is obtained through ClaimStrategy. I omit this object to simplify it. It is not very important for a single producer.
In scenarios with multiple producers, you also need other items to track the serial number. This sequence number refers to the number currently writable. Note that this is different from "adding 1 to the Ring Buffer cursor"-if more than one producer writes data to the Ring Buffer at the same time, some entries may be written by the producer but not submitted yet.
Let's review how to apply for writing nodes. Each producer applies to ClaimStrategy for the next available node. Producer 1 obtains serial number 13, which is the same as that of a single producer above. Producer 2 obtains the serial number 14, although the current cursor of the Ring Buffer only points to 12. This is because ClaimSequence is not only responsible for distributing serial numbers, but also for tracking which serial numbers have been allocated.
Each producer now has its own write node and a brand new serial number.
I green producer 1 and its write nodes, and painted suspicious pink-It looks purple.
Now let's assume that producer 1 still lives in the fairy tale, and for some reason it is not possible to submit data. Producer 2 is ready to submit and sends a request to ProducerBarrier.
As we have seen in the previous commit, ProducerBarrier will only be submitted when the Ring Buffer cursor reaches the previous node of the node to be submitted. In the current situation, the cursor must first reach the number 13 to submit the data of node 14. However, we cannot do this because producer 1 is staring at some of the glittering things and has no time to submit them. Therefore, ClaimStrategy stops at spins until the Ring Buffer cursor reaches the position where it should be.
Now producer 1 wakes up from the confusion and applies to submit the green arrow from data producer 1 of node 13 to represent this request ). ProducerBarrier asked ClaimStrategy to wait for the cursor of the Ring Buffer to reach serial number 12. Of course, it is now. Therefore, the Ring Buffer moves the cursor to 13. Let ProducerBarrier stamp WaitStrategy and tell everyone that the Ring Buffer has been updated. Now ProducerBarrier can complete the request of producer 2, move the Ring Buffer cursor to 14, and notify everyone.
Although the producer completes Data Writing at different times, the content sequence of the Ring Buffer always follows the initial call sequence of nextEntry. That is to say, if a producer is suspended when writing the Ring Buffer, It will be executed immediately only after it is released.
Call --. I finally managed to finish everything and didn't mention the Memory Barrier at once ).
Update: The latest RingBuffer version removes the Producer Barrier. If the ProducerBarrier cannot be found in the code you see, let's assume that when I say "Producer Barrier", I mean "Ring Buffer ".
Update 2: Note that Disruptor 2.0 is named differently from this document. If you are confused about the class name, read the Disruptor 2.0 update summary I wrote.
Http://ifeve.com/disruptor-writing-ringbuffer/.