Springboot integrates the Disruptor as the internal message queue

Posted by DocUK on Mon, 27 Dec 2021 16:15:40 +0100

1, Basic introduction

1. What is a Disruptor?

(1) Disruptor is a high-performance concurrency framework developed by UK foreign exchange trading company LMAX. It can be considered as an efficient and low latency memory message component for inter thread communication. Its biggest feature is high performance. Unlike Kafka and RabbitMQ, which are used for message queuing between services, disruptor is generally used for message delivery between multiple threads in a JVM.
(2) In terms of function, disruptor , implements the function of "queue" and is a bounded queue (in fact, it is a lock free inter thread communication library). Its function is similar to that of , ArrayBlockingQueue , but , disruptor , is much better than , ArrayBlockingQueue , in terms of function and performance.

2. Advantages of Disruptor

(1) Naturally, the most direct application scenario of "Disruptor" is the application of "producer consumer" model. Although we can use "BlockingQueue" of "JDK", the performance of "Disruptor" is about 5 ~ 10 times higher than that of "BlockingQueue":

(2) In other words, what BlockingQueue can do, Disruptor can do better. At the same time, Disruptor can do more:

  • The same "event" can have multiple consumers. Consumers can either process in parallel or depend on each other to form the processing order (form a dependency graph);
  • Pre allocate memory space for storing event content;
  • Extreme optimization and lock free design for extremely high performance objectives;

3. Reasons for high performance of Disruptor

(1) Ring array structure

  • To avoid garbage collection, the {RingBuffer} of the Disruptor} uses an array instead of a linked list. At the same time, the array is more friendly to the processor cache mechanism.

 

(2) Fast element location strategy

  • Because ¢ RingBuffer ¢ is a ring queue structure, it is necessary to specify the size during initialization (that is, the maximum number of slots that can be accommodated in this ring). While ¢ debugger ¢ specifies that the size of ¢ RingBuffer ¢ must be ¢ n ¢ power of ¢ 2 ¢ so that the positioning speed can be accelerated through bit.
  • At the same time, the subscript takes the form of increasing. Don't worry about index overflow. Index , is of type , long , and even the processing speed of , 1 million , QPS , will take , 300000 years to run out.

(3) Lockless design

  • Where it is necessary to ensure that the operation is thread safe, instead of using a lock, the destroyer uses CAS (Compare And Swap/Set) operation. Each producer or consumer thread will first apply for the position of the operable element in the array, and then directly write or read data at that position.

2, Core architecture components of Disruptor

  • Ring Buffer: before version 3.0, Ring Buffer was considered as the core component of Disruptor, but in later versions, it is only responsible for storing and updating data. In some advanced use cases, users can also customize
  • Sequence: the Disruptor uses a set of sequences as a means to identify the processing progress of a specific component (RingBuffer/Consumer). Each consumer and the Disruptor itself maintains a sequence. Although an AtomicLong can also be used to identify progress, another purpose of defining sequences to be responsible for this problem is to prevent CPU cache false sharing between different sequences.
  • Sequencer: sequencer is the real core of Disruptor. This interface has two implementation classes, SingleProducerSequencer and MultiProducerSequencer, which define the concurrent algorithm to transfer data quickly and correctly between producers and consumers.
  • Sequence Barrier: keep references to the sequences of other consumers that the Sequencer and Consumer depend on. In addition, it also defines the logic to determine whether the Consumer still has events to process.
  • Wait Strategy: Wait Strategy determines how a consumer waits for the producer to put events into the Disruptor.
  • Event: the data passed from producer to consumer is called event. It is not a specific type defined by the Disruptor, but is defined and specified by the user of the Disruptor.
  • EventProcessor: holds the Sequence of a specific consumer and has a main event loop to handle the events of the Disruptor. BatchEventProcessor is its specific implementation, implements the event loop, and will call back to the used instance that implements the EventHandler.
  • EventHandler: an interface implemented by the user to handle events. It is the real implementation of the Consumer
  • Producer: producer. It only refers to the user code that calls the Disruptor to publish events. The Disruptor does not define a specific interface or type.

 

 

3, Example

1. pom ladle

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.zhouzy.drools</groupId>
  <artifactId>zzyDrools</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <name>zzyDrools</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  	<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!-- drools Rule engine version -->
        <log4j2.version>2.5</log4j2.version>
    </properties>
     <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
    </parent>

   <!-- Dependency definition -->
    <dependencies>
        <!-- Spring boot start -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
		    <groupId>com.lmax</groupId>
		    <artifactId>disruptor</artifactId>
		    <version>3.3.4</version>
		</dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
 
 		<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.30</version>
		</dependency>
    </dependencies>
  	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

2. Message body

package com.zhouzy.disruptor.model;

public class MessageVo {

	private String code;
	
	private String value;

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}
	
	
}

3. handler that handles the message

package com.zhouzy.disruptor.business;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.lmax.disruptor.EventHandler;
import com.zhouzy.disruptor.model.MessageVo;

public class HelloEventHandler implements EventHandler<MessageVo> {
	Logger log = LoggerFactory.getLogger(HelloEventHandler.class);
    @Override
    public void onEvent(MessageVo event, long sequence, boolean endOfBatch) {
        try {
            //Stopping 1000ms here is to determine that the consumption message is asynchronous
            Thread.sleep(1000);
            log.info("Consumer processing message start");
            if (event != null) {
                log.info("Consumer information is:{}",event);
            }
        } catch (Exception e) {
            log.info("Consumer failed to process message");
        }
        log.info("End of consumer processing message");
    }
}

4. Construct MQManage

package com.zhouzy.disruptor.business;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.zhouzy.disruptor.model.MessageVo;

@Configuration
public class MQManager {

    @SuppressWarnings({ "deprecation", "unchecked" })
	@Bean("MessageVo")
    public RingBuffer<MessageVo> MessageVoRingBuffer() {
        //Define the thread pool for event processing, and the debugger uses Java util. concurrent. Executorservice provides a thread to trigger the event processing of the consumer
        ExecutorService executor = Executors.newFixedThreadPool(2);

        //Specify event factory
        HelloEventFactory factory = new HelloEventFactory();

        //The size of the specified ringbuffer byte must be the nth power of 2 (it can convert the modulo operation to bit operation to improve the efficiency), otherwise the efficiency will be affected
        int bufferSize = 1024 * 256;

        //Single threaded mode for additional performance
        Disruptor<MessageVo> disruptor = new Disruptor<>(factory, bufferSize, executor,
                ProducerType.SINGLE, new BlockingWaitStrategy());

        //Set event business processor - Consumer
        disruptor.handleEventsWith(new HelloEventHandler());

        // Start the disruptor thread
        disruptor.start();

        //Get the ringbuffer ring, which is used to receive events produced by the producer
        RingBuffer<MessageVo> ringBuffer = disruptor.getRingBuffer();

        return ringBuffer;
    }
}

5. Producer

public interface DisruptorMqService {

    /**
     * news
     * @param message
     */
    void sayHelloMq(String message);
}

@Component
@Service
public class DisruptorMqServiceImpl implements DisruptorMqService {
	Logger log = LoggerFactory.getLogger(DisruptorMqServiceImpl.class);

    @Autowired
    private RingBuffer<MessageVo> MessageVoRingBuffer;


    @Override
    public void sayHelloMq(String message) {
        log.info("record the message: {}",message);
        //Gets the subscript of the next Event slot
        long sequence = MessageVoRingBuffer.next();
        try {
            //Fill Event with data
            MessageVo event = MessageVoRingBuffer.get(sequence);
            event.setValue(message);
            log.info("To add a message to the message queue:{}", event);
        } catch (Exception e) {
            log.error("failed to add event to MessageVoRingBuffer for : e = {},{}",e,e.getMessage());
        } finally {
            //Publish the Event, activate the observer to consume, and pass the sequence to the consumer
            //Note that the last publish method must be placed in finally to ensure that it must be called; If a requested sequence is not submitted, subsequent publishing operations or other producer s will be blocked
            MessageVoRingBuffer.publish(sequence);
        }
    }
}

6. Unit test

package com.zhouzy.disruptor;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.zhouzy.disruptor.business.DisruptorMqService;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = DisruptorApplication.class)
public class DemoApplicationTests {

	Logger log = LoggerFactory.getLogger(DemoApplicationTests.class);
	
    @Autowired
    private DisruptorMqService disruptorMqService;
    /**
     * The project uses the Disruptor as the message queue
     * @throws Exception
     */
    @Test
    public void sayHelloMqTest() throws Exception{
        disruptorMqService.sayHelloMq("Here comes the news, Hello world!");
        log.info("The message queue has been sent");
        //The 2000ms stop here is to make sure that the message processing is asynchronous
        Thread.sleep(2000);
    }
}

4, Test results

 

Topics: Spring Boot disruptor