Spring Boot + Disruptor implementa filas de mensagens e informa o que é rápido e eficiente!

01. Plano de fundo

No trabalho, encontrei um projeto que usava o Disruptor como fila de mensagens. Você leu certo, não é Kafka, nem RabbitMQ; a maior vantagem do Disruptor é que ele é rápido e é open source. Vamos fazer um registro simples abaixo.

02. Introdução ao Disruptor

  1. Disruptor é uma fila de alto desempenho desenvolvida pela LMAX, uma empresa britânica de comércio de câmbio.A intenção original de pesquisa e desenvolvimento era resolver o problema de atraso das filas de memória (em testes de desempenho, constatou-se que está na mesma ordem de magnitude como operações de E/S). O sistema desenvolvido com base no Disruptor pode suportar 6 milhões de pedidos por segundo em um único thread. Depois de discursar na QCon em 2010, ganhou a atenção da indústria.
  2. Disruptor é uma estrutura Java de código aberto projetada para atingir o maior rendimento possível (TPS) e a menor latência possível no problema produtor-consumidor (PCP).
  3. Do ponto de vista funcional, o Disruptor implementa a função “fila” e é uma fila limitada. Então seu cenário de aplicação é naturalmente o cenário de aplicação do modelo “produtor-consumidor”.
  4. O Disruptor é um componente chave da plataforma de negociação on-line LMAX. A plataforma LMAX usa essa estrutura para processar pedidos a uma velocidade de 6 milhões de TPS. Além da área financeira, o Disruptor pode ser usado em outras aplicações gerais. Ele pode trazer benefícios significativos • Melhorias de desempenho.
  5. Na verdade, o Disruptor não é tanto uma estrutura, mas uma ideia de design. Para programas com elementos como "simultaneidade, buffers, modelos produtor-consumidor e processamento de transações", o Disruptor propõe uma ideia de design em grande escala. Soluções para melhorar o desempenho ( TPS).
  6. Página inicial do github do Disruptor: https://github.com/LMAX-Exchange/disruptor

03. O conceito central do Disruptor

Vamos começar entendendo os conceitos básicos do Disruptor para entender como ele funciona. O modelo conceitual apresentado a seguir é um objeto de domínio e um objeto central mapeado para implementação de código.

04、Buffer de anel

Como o nome sugere, um buffer circular. RingBuffer costumava ser o objeto principal do Disruptor, mas a partir da versão 3.0 suas responsabilidades foram simplificadas para ser responsável apenas pelo armazenamento e atualização dos dados (eventos) trocados através do Disruptor. Em alguns cenários de aplicação mais avançados, o Ring Buffer pode ser completamente substituído pela implementação definida pelo usuário.

05、Disruptor de sequência

Os dados (eventos) trocados por meio dele são numerados e gerenciados por meio de números de série crescentes sequencialmente, e o processamento dos dados (eventos) é sempre processado de forma incremental ao longo do número de série. Uma sequência é usada para rastrear o progresso do processamento de um manipulador de eventos específico (RingBuffer/Consumer).

Embora um AtomicLong também possa ser usado para identificar o progresso, definir uma Sequência como responsável por este problema tem outro propósito, que é evitar o problema de falso compartilhamento do cache da CPU (Flase Sharing) entre diferentes Sequências. (Nota: Este é um dos pontos-chave para o Disruptor atingir alto desempenho. Já existem muitas introduções ao problema do pseudo-compartilhamento na Internet, então não entrarei em detalhes aqui).

06、Sequenciador

O sequenciador é o verdadeiro núcleo do Disruptor. Esta interface possui duas classes de implementação, SingleProducerSequencer e MultiProducerSequencer, que definem algoritmos de simultaneidade para transferência rápida e correta de dados entre produtores e consumidores.

07、Barreira de Sequência

Usado para manter referências à principal sequência publicada do RingBuffer e às sequências de outros consumidores dos quais o consumidor depende. A Barreira de Sequência também define a lógica que determina se o Consumidor possui eventos que podem ser processados.

08、Estratégia de Espera

Defina a estratégia de como o Consumidor aguarda o próximo evento. (Observação: o Disruptor define uma variedade de estratégias diferentes, proporcionando desempenho diferente para cenários diferentes)

09、Evento

Na semântica do Disruptor, os dados trocados entre produtores e consumidores são chamados de eventos. Não é um tipo específico definido pelo Disruptor, mas é definido e especificado pelo usuário do Disruptor.

10、Processador de Eventos

EventProcessor mantém a sequência de um consumidor específico (Consumer) e fornece um loop de eventos (Event Loop) para chamar a implementação de processamento de eventos.

11. Manipulador de Eventos

A interface de processamento de eventos definida pelo Disruptor é implementada pelo usuário e utilizada para processar eventos, sendo a implementação real do Consumer.

12、Produtor

Ou seja, o produtor geralmente se refere ao código do usuário que chama o Disruptor para publicar eventos. O Disruptor não define uma interface ou tipo específico.

13. Demonstração de caso

Recomende um tutorial básico do Spring Boot e exemplos práticos: https://github.com/javastacks/spring-boot-best-practice

Através das 8 etapas a seguir, você pode obter o Disruptor Get home:

1. Adicione dependência pom.xml

<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.4</version>
</dependency>

2. Modelo de corpo da mensagem

/**
 * 消息体
 */
@Data
public class MessageModel {
    private String message;
}

3. Construir EventFactory

public class HelloEventFactory implements EventFactory<MessageModel> {
    @Override
    public MessageModel newInstance() {
        return new MessageModel();
    }
}

4. Construa o consumidor EventHandler

@Slf4j
public class HelloEventHandler implements EventHandler<MessageModel> {
    @Override
    public void onEvent(MessageModel event, long sequence, boolean endOfBatch) {
        try {
            //这里停止1000ms是为了确定消费消息是异步的
            Thread.sleep(1000);
            log.info("消费者处理消息开始");
            if (event != null) {
                log.info("消费者消费的信息是:{}",event);
            }
        } catch (Exception e) {
            log.info("消费者处理消息失败");
        }
        log.info("消费者处理消息结束");
    }
}

5. Construir BeanManager

/**
 * 获取实例化对象
 */
@Component
public class BeanManager implements ApplicationContextAware {

    private static ApplicationContext applicationContext = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() { return applicationContext; }

    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }

    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }
}

6. Construir MQManager

@Configuration
public class MQManager {

    @Bean("messageModel")
    public RingBuffer<MessageModel> messageModelRingBuffer() {
        //定义用于事件处理的线程池, Disruptor通过java.util.concurrent.ExecutorSerivce提供的线程来触发consumer的事件处理
        ExecutorService executor = Executors.newFixedThreadPool(2);

        //指定事件工厂
        HelloEventFactory factory = new HelloEventFactory();

        //指定ringbuffer字节大小,必须为2的N次方(能将求模运算转为位运算提高效率),否则将影响效率
        int bufferSize = 1024 * 256;

        //单线程模式,获取额外的性能
        Disruptor<MessageModel> disruptor = new Disruptor<>(factory, bufferSize, executor,
                ProducerType.SINGLE, new BlockingWaitStrategy());

        //设置事件业务处理器---消费者
        disruptor.handleEventsWith(new HelloEventHandler());

        // 启动disruptor线程
        disruptor.start();

        //获取ringbuffer环,用于接取生产者生产的事件
        RingBuffer<MessageModel> ringBuffer = disruptor.getRingBuffer();

        return ringBuffer;
    }

7. Construa Mqservice e implemente o produtor de classe

public interface DisruptorMqService {

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

@Slf4j
@Component
@Service
public class DisruptorMqServiceImpl implements DisruptorMqService {

    @Autowired
    private RingBuffer<MessageModel> messageModelRingBuffer;

    @Override
    public void sayHelloMq(String message) {
        log.info("record the message: {}",message);
        //获取下一个Event槽的下标
        long sequence = messageModelRingBuffer.next();
        try {
            //给Event填充数据
            MessageModel event = messageModelRingBuffer.get(sequence);
            event.setMessage(message);
            log.info("往消息队列中添加消息:{}", event);
        } catch (Exception e) {
            log.error("failed to add event to messageModelRingBuffer for : e = {},{}",e,e.getMessage());
        } finally {
            //发布Event,激活观察者去消费,将sequence传递给改消费者
            //注意最后的publish方法必须放在finally中以确保必须得到调用;如果某个请求的sequence未被提交将会堵塞后续的发布操作或者其他的producer
            messageModelRingBuffer.publish(sequence);
        }
    }
}

8. Construa classes e métodos de teste

Não vou apresentar o básico do Spring Boot, recomendo este tutorial prático: https://github.com/javastacks/spring-boot-best-practice

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

    @Autowired
    private DisruptorMqService disruptorMqService;
    /**
     * 项目内部使用Disruptor做消息队列
     * @throws Exception
     */
    @Test
    public void sayHelloMqTest() throws Exception{
        disruptorMqService.sayHelloMq("消息到了,Hello world!");
        log.info("消息队列已发送完毕");
        //这里停止2000ms是为了确定是处理消息是异步的
        Thread.sleep(2000);
    }
}

Resultados da execução de teste

2020-04-05 14:31:18.543  INFO 7274 --- [           main] c.e.u.d.d.s.Impl.DisruptorMqServiceImpl  : record the message: 消息到了,Hello world!
2020-04-05 14:31:18.545  INFO 7274 --- [           main] c.e.u.d.d.s.Impl.DisruptorMqServiceImpl  : 往消息队列中添加消息:MessageModel(message=消息到了,Hello world!)
2020-04-05 14:31:18.545  INFO 7274 --- [           main] c.e.utils.demo.DemoApplicationTests      : 消息队列已发送完毕
2020-04-05 14:31:19.547  INFO 7274 --- [pool-1-thread-1] c.e.u.d.disrupMq.mq.HelloEventHandler    : 消费者处理消息开始
2020-04-05 14:31:19.547  INFO 7274 --- [pool-1-thread-1] c.e.u.d.disrupMq.mq.HelloEventHandler    : 消费者消费的信息是:MessageModel(message=消息到了,Hello world!)
2020-04-05 14:31:19.547  INFO 7274 --- [pool-1-thread-1] c.e.u.d.disrupMq.mq.HelloEventHandler    : 消费者处理消息结束

14. Resumo

Na verdade, o modelo gerador->consumidor é muito comum e os efeitos acima podem ser facilmente alcançados por meio de algumas filas de mensagens. A diferença é que o Disruptor é implementado como uma fila na memória e não tem bloqueios.

É por isso que o Disruptor é eficiente.

Declaração de direitos autorais: Este artigo é um artigo original do blogueiro CSDN "Fuer Tianci" e segue o acordo de direitos autorais CC 4.0 BY-SA. Anexe o link da fonte original e esta declaração ao reimprimir. Link original: https://blog.csdn.net/buertianci/article/details/105327031

Artigos populares recentes recomendados:

1. Compilação de mais de 1.000 perguntas e respostas de entrevistas Java (versão mais recente de 2022)

2. Explosivo! As corrotinas Java estão chegando. . .

3. Tutorial do Spring Boot 2.x, tão completo!

4. Pare de encher a tela com categorias explosivas e experimente o modo decorador. Esse é o jeito elegante! !

5. "Java Development Manual (Songshan Edition)" foi lançado recentemente, baixe-o rapidamente!

Se você acha que é bom, não esqueça de curtir e retuitar!

Acho que você gosta

Origin blog.csdn.net/youanyyou/article/details/133032721
Recomendado
Clasificación