Envio da mensagem do RabbitMQ com o Spring AMQP
1. Introdução
Neste tutorial, vamos explorar o conceito defanoute trocas de tópicos comSpring AMQPeRabbitMQ.
Em um nível alto,fanout exchanges irábroadcast the same message to all bound queues, enquantotopic exchanges usará uma chave de roteamento parapassing messages to a particular bound queue or queues.
A leitura anterior deMessaging With Spring AMQP é recomendada para este tutorial.
2. Configurando uma troca de fãs
Vamos configurar um fanout exchange com duas filas vinculadas a ele. Quando enviamos uma mensagem para essa troca, as duas filas receberão a mensagem. Nossa troca de fanout ignora qualquer chave de roteamento incluída na mensagem.
Spring AMQP nos permite agregar todas as declarações de filas, trocas e ligações em um objetoDeclarables:
@Bean
public Declarables fanoutBindings() {
Queue fanoutQueue1 = new Queue("fanout.queue1", false);
Queue fanoutQueue2 = new Queue("fanout.queue2", false);
FanoutExchange fanoutExchange = new FanoutExchange("fanout.exchange");
return new Declarables(
fanoutQueue1,
fanoutQueue2,
fanoutExchange,
bind(fanoutQueue1).to(fanoutExchange),
BindingBuilder.bind(fanoutQueue2).to(fanoutExchange));
}
3. Configurando uma troca de tópicos
Agora, também configuraremos uma troca de tópicos com duas filas, cada uma com um padrão de ligação diferente:
@Bean
public Declarables topicBindings() {
Queue topicQueue1 = new Queue(topicQueue1Name, false);
Queue topicQueue2 = new Queue(topicQueue2Name, false);
TopicExchange topicExchange = new TopicExchange(topicExchangeName);
return new Declarables(
topicQueue1,
topicQueue2,
topicExchange,
BindingBuilder
.bind(topicQueue1)
.to(topicExchange).with("*.important.*"),
BindingBuilder
.bind(topicQueue2)
.to(topicExchange).with("#.error"));
}
A topic exchange allows us to bind queues to it with different key patterns. Isso é muito flexível e nos permite vincular várias filas com o mesmo padrão ou até mesmo vários padrões à mesma fila.
Quando a chave de roteamento da mensagem corresponder ao padrão, ela será colocada na fila. If a queue has multiple bindings which match the message’s routing key, only one copy of the message is placed on the queue.
Nossos padrões de encadernação podem usar um asterisco ("*") para corresponder a uma palavra em uma posição específica ou um sinal de libra ("#") para corresponder a zero ou mais palavras.
Portanto, nossotopicQueue1 receberá mensagens com chaves de roteamento com um padrão de três palavras, sendo a palavra do meio "importante" - por exemplo:“user.important.error” ou“blog.important.notification”.
E, nossotopicQueue2 receberá mensagens que possuem chaves de roteamento terminando com a palavra erro; exemplos correspondentes são“error”,“user.important.error” ou“blog.post.save.error”.
4. Configurando um produtor
Usaremos o métodoconvertAndSend deRabbitTemplate para enviar nossas mensagens de amostra:
String message = " payload is broadcast";
return args -> {
rabbitTemplate.convertAndSend(FANOUT_EXCHANGE_NAME, "", "fanout" + message);
rabbitTemplate.convertAndSend(TOPIC_EXCHANGE_NAME, ROUTING_KEY_USER_IMPORTANT_WARN,
"topic important warn" + message);
rabbitTemplate.convertAndSend(TOPIC_EXCHANGE_NAME, ROUTING_KEY_USER_IMPORTANT_ERROR,
"topic important error" + message);
};
ORabbitTemplate fornece muitos métodosconvertAndSend() sobrecarregados para diferentes tipos de troca.
Quando enviamos uma mensagem para uma troca de fanout, a chave de roteamento é ignorada e a mensagem é passada para todas as filas ligadas.
Quando enviamos uma mensagem para a troca de tópicos, precisamos passar uma chave de roteamento. Com base nessa chave de roteamento, a mensagem será entregue em filas específicas.
5. Configurando Consumidores
Finalmente, vamos configurar quatro consumidores - um para cada fila - para pegar as mensagens produzidas:
@RabbitListener(queues = {FANOUT_QUEUE_1_NAME})
public void receiveMessageFromFanout1(String message) {
System.out.println("Received fanout 1 message: " + message);
}
@RabbitListener(queues = {FANOUT_QUEUE_2_NAME})
public void receiveMessageFromFanout2(String message) {
System.out.println("Received fanout 2 message: " + message);
}
@RabbitListener(queues = {TOPIC_QUEUE_1_NAME})
public void receiveMessageFromTopic1(String message) {
System.out.println("Received topic 1 (" + BINDING_PATTERN_IMPORTANT + ") message: " + message);
}
@RabbitListener(queues = {TOPIC_QUEUE_2_NAME})
public void receiveMessageFromTopic2(String message) {
System.out.println("Received topic 2 (" + BINDING_PATTERN_ERROR + ") message: " + message);
}
We configure consumers using the @RabbitListener annotation. O único argumento passado aqui é o nome das filas. Os consumidores não estão cientes aqui de trocas ou chaves de roteamento.
6. Executando o Exemplo
Nosso projeto de amostra é um aplicativo Spring Boot e, portanto, inicializará o aplicativo juntamente com uma conexão com o RabbitMQ e configurará todas as filas, trocas e ligações.
Por padrão, nosso aplicativo espera uma instância RabbitMQ em execução no host local na porta 5672. Podemos modificar este e outros padrões emapplication.yaml.
Nosso projeto expõe o endpoint HTTP no URI -/broadcast - que aceita POSTs com uma mensagem no corpo da solicitação.
Quando enviamos uma solicitação para esse URI com o corpo "Test", devemos ver algo semelhante a este na saída:
Received fanout 1 message: fanout payload is broadcast
Received topic 1 (*.important.*) message: topic important warn payload is broadcast
Received topic 2 (#.error) message: topic important error payload is broadcast
Received fanout 2 message: fanout payload is broadcast
Received topic 1 (*.important.*) message: topic important error payload is broadcast
A ordem em que veremos essas mensagens não é, obviamente, garantida.
7. Conclusão
Neste tutorial rápido, abordamos trocas de fanout e tópicos com Spring AMQP e RabbitMQ.
O código-fonte completo e todos os trechos de código para este tutorial estão disponíveis noGitHub repository.