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
- 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.
- 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).
- 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”.
- 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.
- 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).
- 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!
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!