1. Breve introdução
MQ
O nome completo (Message Queue), também conhecido como message queue, é um middleware para comunicação assíncrona. Pode ser entendido como uma agência postal. O remetente entrega a mensagem para a agência postal e, em seguida, a agência postal nos ajudará a enviá-la para o destinatário específico da mensagem (consumidor). Não precisamos nos preocupar com o processo de envio específico e tempo, e não vai interferir com minhas outras coisas. Comum MQ
há kafka
, activemq
, zeromq
, rabbitmq
e assim por diante.
RabbitMQ
É um AMQP
protocolo erlanng
desenvolvido por uma linguagem orientada para alta simultaneidade , utilizado em mensagens em tempo real com requisitos de alta confiabilidade, e oferece suporte a clientes multilíngues.
Conceito básico
Broker
: Simplificando, é a entidade do servidor de fila de mensagensExchange
: Troca de mensagens, que especifica as regras e filas para as quais as mensagens são encaminhadasQueue
: Portadora da fila de mensagens, cada mensagem será colocada em uma ou mais filasBinding
: Vinculativo, sua função é vinculá-losexchange
equeue
vinculá-los de acordo com as regras de roteamentoRouting Key
: Palavra-chave de roteamento, deexchange
acordo com esta palavra-chave para entrega de mensagensvhost
: Host virtual,broker
pode configurar vários em umvhost
, usado para separar as permissões de diferentes usuáriosproducer
: O produtor da mensagem é o programa que entrega a mensagemconsumer
: O consumidor da mensagem é o programa que recebe a mensagemchannel
: Canal de mensagem, em cada conexão do cliente, múltiplos podem ser estabelecidoschannel
, cadachannel
um representando uma tarefa de sessão
Cenários de referência comuns
- Envio de e-mail: Após o registro do usuário, a mensagem será entregue ao usuário
rabbitmq
, e o consumidor da mensagem enviará o e-mail de forma assíncrona, o que melhora a velocidade de resposta do sistema - Corte de pico de tráfego: geralmente, é amplamente usado na atividade de pico. O pico fará com que o aplicativo trave devido ao tráfego excessivo. Para resolver esse problema, a fila de mensagens geralmente é adicionada ao front end do aplicativo. É usado para controlar o número de atividades e os pedidos que excedem esse determinado limite são descartados diretamente. Alivie aplicações de britagem de alto fluxo de curto prazo.
- Tempo limite do pedido: O uso
rabbitmq
da fila de atraso pode facilmente realizar a função de tempo limite do pedido, por exemplo, o usuário cancela o pedido sem pagar 30 minutos após colocar o pedido
2. Prepare o ambiente RabbitMQ
Devido ao RabbitMQ
problema de instalação, o teste aqui usa o ambiente docker como exemplo. Linux
Instale o docker primeiro ; se não, você pode consultar os comandos comuns de instalação / docker do docker
Em seguida, baixe e inicieRabbitMQ
docker run -d --hostname my-rabbit --name pikachues-rabbit -p 5672:5672 -p15672:15672 rabbitmq:3-management
3. Teste
RabbitMQ
Existem quatro trocas de mensagens. Eles são diretos (padrão), fanout, tópico e cabeçalhos. Diferentes tipos de opções têm diferentes estratégias de encaminhamento de mensagens. Os springboot
quatro tipos de troca de mensagens a seguir são testados.
0. Prepare o ambiente de teste
confiar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Arquivo de configuração
spring.rabbitmq.host=rabbitmq安装的ip地址
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.port=5672
1.direcionar
A troca direta, que funciona de maneira semelhante ao unicast, Exchange
envia mensagens para ROUTING_KEY
uma Fila que corresponda exatamente .
configuração direta
@Configuration
public class RabbitDirectConfig {
public final static String DIRECTNAME = "pikachues-direct";
@Bean
Queue queue() {
return new Queue("hello.pikachues");
}
@Bean
DirectExchange directExchange(){
return new DirectExchange(DIRECTNAME,true,false);
}
@Bean
Binding binding(){
return BindingBuilder.bind(queue()).to(directExchange()).with("direct");
}
}
Recebendo mensagem
@Component
public class DirectReceiver {
@RabbitListener(queues = "hello.pikachues")
public void handler1(String msg){
System.out.println(">>>handler"+msg);
}
}
teste
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitmqDemoApplicationTests {
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void direct() {
rabbitTemplate.convertAndSend("hello.pikachues","hello pikachues");
}
}
2.fanout
Broadcasting é um tipo de switch, independente da ROUTING_KEY
configuração da mensagem , Exchange
ele irá encaminhar a mensagem para todos os vinculados Queue
.
configuração fanout
@Configuration
public class RabbitFanoutConfig {
public static final String FANOUTNAME = "pikachues-fanout";
@Bean
Queue queueOne(){
return new Queue("queue-one");
}
@Bean
Queue queueTwo(){
return new Queue("queue-two");
}
@Bean
FanoutExchange fanoutExchange(){
return new FanoutExchange(FANOUTNAME,true,false);
}
@Bean
Binding bindingOne(){
return BindingBuilder.bind(queueOne()).to(fanoutExchange());
}
@Bean
Binding bindingTwo(){
return BindingBuilder.bind(queueTwo()).to(fanoutExchange());
}
}
Recebendo mensagem
@Component
public class FanoutReceiver {
@RabbitListener(queues = "queue-one")
public void handler1(String msg){
System.out.println("FanoutReceiver:handler1"+msg);
}
@RabbitListener(queues = "queue-two")
public void handler2(String msg){
System.out.println("FanoutReceiver:handler2"+msg);
}
}
teste
@Test
public void testFanout(){
rabbitTemplate.convertAndSend(RabbitFanoutConfig.FANOUTNAME,null,"hello Fanout");
}
3. tópico
O trocador de tema funciona como multicast, Exchange
encaminha as mensagens e ROUTING_KEY
o mesmo padrão corresponde a todas as filas, ROUTING_KEY
como user.stock
o Message
encaminhamento para corresponder ao modo de ligação * .stock, user.stock, *. * E # .user .stock. # A fila. (* A tabela deve corresponder a uma frase arbitrária, # significa corresponder a 0 ou mais frases)
configuração do tópico
@Configuration
public class RabbitTopicConfig {
public static final String TOPICNAME = "pikachues-topic";
@Bean
TopicExchange topicExchange(){
return new TopicExchange(TOPICNAME,true,false);
}
@Bean
Queue xiaomi(){
return new Queue("xiaomi");
}
@Bean
Queue huaiwei(){
return new Queue("huawei");
}
@Bean
Queue phone(){
return new Queue("phone");
}
@Bean
Binding xiaomiBinding(){
return BindingBuilder.bind(xiaomi()).to(topicExchange()).with("xiaomi.#");
}
@Bean
Binding huaiweiBinding(){
return BindingBuilder.bind(huaiwei()).to(topicExchange()).with("huawei.#");
}
@Bean
Binding phoneBinding(){
return BindingBuilder.bind(phone()).to(topicExchange()).with("#.phone.#");
}
}
Recebendo mensagem
@Component
public class TopicReceiver {
@RabbitListener(queues = "xiaomi")
public void handler1(String msg) {
System.out.println("TopicReceiver:handler1:" + msg);
}
@RabbitListener(queues = "huawei")
public void handler2(String msg) {
System.out.println("TopicReceiver:handler2:" + msg);
}
@RabbitListener(queues = "phone")
public void handler3(String msg) {
System.out.println("TopicReceiver:handler3:" + msg);
}
}
teste
@Test
public void testTopic(){
rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME,"xiaomi.news","小米新闻");
rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME,"vivo.phone","小米手机");
rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME,"huawei.phone","华为手机");
}
4. cabeçalhos
O cabeçalho do corpo da mensagem corresponde (ignorar)
configuração de cabeçalhos
@Configuration
public class RabbitHeaderConfig {
public static final String HEADERNAME = "pikachues-header";
@Bean
HeadersExchange headersExchange(){
return new HeadersExchange(HEADERNAME, true,false);
}
@Bean
Queue queueName(){
return new Queue("name-queue");
}
@Bean
Queue queueAge(){
return new Queue("age-queue");
}
@Bean
Binding bindingName(){
Map<String,Object> map = new HashMap<>();
map.put("name","pikachues");
return BindingBuilder.bind(queueName()).to(headersExchange()).whereAny(map).match();
}
@Bean
Binding bindingAge(){
return BindingBuilder.bind(queueAge()).to(headersExchange()).where("age").exists();
}
}
Recebendo mensagem
@Component
public class HeaderReceiver {
@RabbitListener(queues = "name-queue")
public void handler1(byte[] msg) {
System.out.println("HeaderReceiver:handler1:" + new String(msg, 0, msg.length));
}
@RabbitListener(queues = "age-queue")
public void handler2(byte[] msg) {
System.out.println("HeaderReceiver:handler2:" + new String(msg, 0, msg.length));
}
}
teste
@Test
public void testHeaders(){
Message nameMsg = MessageBuilder.withBody("hello pikachues".getBytes()).setHeader("name","pikachues").build();
Message ageMsg = MessageBuilder.withBody("hello 99".getBytes()).setHeader("age","99").build();
rabbitTemplate.convertAndSend(RabbitHeaderConfig.HEADERNAME,null,nameMsg);
rabbitTemplate.convertAndSend(RabbitHeaderConfig.HEADERNAME,null,ageMsg);
}
4. Outro
Se você quiser entender RabbitMQ
os princípios básicos, leia este artigo ou a introdução do site oficial do springboot .