Integração simples de RabbitMQ e Spring Boot

Uma tradução do artigo foi preparada antes do início do curso "Developer on the Spring Framework" .




Olá a todos!

Eu gostaria de compartilhar com você uma biblioteca de código aberto que facilita a integração do RabbitMQ com aplicativos no Spring Boot. Além disso, esta biblioteca oferece um novo conceito aprimorado de novas tentativas (em comparação com a abordagem padrão do Spring AMQP).
Simplifica? Mas como?


Configuração automática


Suponha que desejemos implementar a interação (por exemplo, usando SSL) com o RabbitMQ usando o Spring AMQP. Precisamos criar um pouco de feijão, como ConnectionFactory, RabbitAdmin, RabbitTemplatee AbstractRabbitListenerContainerFactory.

Mas imagine que existem vários hosts virtuais no RabbitMQ. Por padrão, no Spring AMQP, você precisa criar um ecossistema para cada host virtual com configuração manual de RabbitTemplatee RabbitListener.



Como você pode ver, toda essa configuração leva tempo e pode ser uma dor de cabeça.

Ciente desse problema, a biblioteca proposta configura automaticamente todos esses beans para você. A única coisa que você precisa fazer é determinar a configuração application.propertiese a mágica acontecerá!
Uau, mas como é a configuração application.properties?

Tudo é muito simples. Em primeiro lugar, há um prefixo para as propriedades - isso spring.rabbitmq.custom.

Depois disso, você deve especificar o nome do evento. Essa é uma parte muito importante, pois todas as configurações que a biblioteca faz são baseadas nesse nome.

Após o nome do evento, você pode especificar a propriedade e o valor. Todas as propriedades são descritas na documentação .

Portanto, o modelo é o seguinte: Abaixo está um exemplo de configuração para duas conexões diferentes.

spring.rabbitmq.custom.<YOUR_EVENT>.<PROPERTY>=<VALUE>




spring.rabbitmq.custom.some-event.host=localhost
spring.rabbitmq.custom.some-event.port=5672
spring.rabbitmq.custom.some-event.ttlRetryMessage=5000
spring.rabbitmq.custom.some-event.maxRetriesAttempts=5
spring.rabbitmq.custom.some-event.ttlMultiply=2
spring.rabbitmq.custom.some-event.queueRoutingKey=ROUTING.KEY.TEST
spring.rabbitmq.custom.some-event.exchange=ex.custom.direct
spring.rabbitmq.custom.some-event.exchangeType=direct
spring.rabbitmq.custom.some-event.queue=queue.custom.test
spring.rabbitmq.custom.some-event.autoCreate=true
spring.rabbitmq.custom.some-event.concurrentConsumers=1
spring.rabbitmq.custom.some-event.maxConcurrentConsumers=1
spring.rabbitmq.custom.some-event.virtualHost=tradeshift
spring.rabbitmq.custom.some-event.primary=true
spring.rabbitmq.custom.some-event.sslConnection=true
spring.rabbitmq.custom.some-event.tlsKeystoreLocation=file:///etc/tradeshift/your-service/tls-keystore.pkcs12
spring.rabbitmq.custom.some-event.tlsKeystorePassword=${RABBITMQ_PASS_CERT}

spring.rabbitmq.custom.another-event.host=localhost
spring.rabbitmq.custom.another-event.port=5672
spring.rabbitmq.custom.another-event.ttlRetryMessage=5000
spring.rabbitmq.custom.another-event.maxRetriesAttempts=5
spring.rabbitmq.custom.another-event.queueRoutingKey=TEST.QUEUE
spring.rabbitmq.custom.another-event.exchange=ex_test_1
spring.rabbitmq.custom.another-event.exchangeType=direct
spring.rabbitmq.custom.another-event.queue=queue_test_1
spring.rabbitmq.custom.another-event.autoCreate=true
spring.rabbitmq.custom.another-event.concurrentConsumers=1
spring.rabbitmq.custom.another-event.maxConcurrentConsumers=1
spring.rabbitmq.custom.another-event.username=guest
spring.rabbitmq.custom.another-event.password=${RABBITMQ_PASS}

Como você pode ver, não escrevemos uma única linha de código !!!
Bom, mas você estava dizendo algo sobre a nova estratégia de repetição, certo?

Sim, meu amigo disse!

Mas antes de explicar essa nova estratégia, vamos dar uma olhada no comportamento padrão do RabbitMQ e do Spring AMQP.

Por padrão, o RabbitMQ não fornece processamento de nova tentativa que permitiria controlar todo o ciclo de vida da mensagem.

Por exemplo, antes do RabbitMQ 3.8 , o cabeçalho da mensagem não tinha uma propriedade para controlar o número de tentativas feitas.

Comportamento padrão RabbitMQ:

  • Se você não definiu o tempo de vida (TTL, tempo de vida), o RabbitMQ tentará constantemente enfileirar sua mensagem.
  • Se você definiu TTL, mas não definiu dlx , depois do TTL, a mensagem será removida da fila e você a perderá.
  • Se você definiu TTL e dlx , depois do TTL, a mensagem será enviada ao trocador definido em dlx.


Comportamento padrão RabbitMQ

Mas e se quisermos um TTL crescente (por exemplo, em caso de operação instável) e controlar o número de novas tentativas?

Ótima pergunta!

Está na hora de explicar como as bibliotecas Spring AQMP e Spring Rabbit Tuning funcionam!

Estratégias de repetição


Spring AMQP por padrão


Ao usar o Spring AMQP por padrão, você pode definir opções de nova tentativa usando as propriedades abaixo.

Mas essa abordagem tem problemas. Por padrão, o Spring AMQP bloqueará sua fila quando você tentar entregar uma mensagem novamente.

Esse problema pode ser resolvido de forma indireta: use paralelismo. Mas, dessa maneira, carregamos a JVM, e essa não é a melhor abordagem. Se você tiver cinco mensagens problemáticas (como no exemplo), um gargalo surgirá novamente, e ainda precisamos determinar manualmente os beans na @Configurationcaixa - para cada conexão e contêiner.

spring.rabbitmq.listener.simple.retry.enabled=true
spring.rabbitmq.listener.simple.retry.initial-interval=2000
spring.rabbitmq.listener.simple.retry.max-attempts=5
spring.rabbitmq.listener.simple.retry.multiplier=2
spring.rabbitmq.listener.simple.max-concurrency=5
spring.rabbitmq.listener.simple.concurrency=5


Coelho da Primavera


Esta biblioteca adota uma abordagem diferente com uma fila separada para novas tentativas. Dessa forma, podemos controlar o TTL usando o parâmetro de mensagem de expiração e o parâmetro x-death para controlar o número de novas tentativas.
Mas como?

Usamos o conceito dlx na fila de repetição para reenviar a mensagem para a fila principal. Assim, temos acesso ao parâmetro x-death e podemos determinar programaticamente o tempo de vida da mensagem.

Nota. Como essa biblioteca é uma extensão do Spring AMQP, você pode usar a estratégia de repetição padrão e usar essa biblioteca apenas para configuração automática de beans.


Repetição de ajuste de coelho
Posso usar sua estratégia de repetição sem configuração automática? Eu já tenho muitos feijões e não tenho tempo para reescrevê-los.

Sabemos que o mundo não é perfeito e, para essa situação, temos um sinalizador que permite desativar a configuração automática e usar apenas nossa abordagem para o processamento de repetições e outras vantagens, como gerenciamento de lixeira.

Você pode desativar a configuração automática usando a seguinte propriedade:

spring.rabbitmq.enable.custom.autoconfiguration=false

Mas tenho duas conexões diferentes, o que devo fazer neste caso?

Se você possui mais de uma conexão e deseja desativar a configuração automática, precisará especificar o nome do bean RabbitTemplatepara cada conexão. Isto é descrito aqui . Você ainda pode usar nosso bean para facilitar o trabalho com o Spring AMQP.

spring.rabbitmq.custom.<YOUR_EVENT>.rabbitTemplateBeanName= <RABBITTEMPLATE_BEAN_NAME>

RabbitTemplateHandler
Muito bem! Então, eu quero usar isso. Como posso fazer isso? Você tem um exemplo ou documentação?
Sim!

Temos um projeto de exemplo neste repositório , onde você pode ver como usar esta biblioteca para editores e assinantes.

Mas é tão simples que vou dar um exemplo aqui!

Editor


Há uma classe na biblioteca RabbitTemplateHandlere é muito fácil de usar. Você precisa chamar o método getRabbitTemplatee transmitir o host virtual como um parâmetro para obter o bean RabbitTemplate. Depois disso, você pode chamar o método convertAndSend e passá-lo nos parâmetros da chave de troca e roteamento.

Nota : a chave de troca e roteamento pode ser obtida usando a anotaçãoValor.

Um exemplo :

@Value("${spring.rabbitmq.custom.some-event.exchange}")
private String exchangeSomeEvent;

@Value("${spring.rabbitmq.custom.some-event.queueRoutingKey}")
private String routingKeySomeEvent;

@Autowired
private final RabbitTemplateHandler rabbitTemplateHandler;

public void sendMessage(final String message) {
   rabbitTemplateHandler.getRabbitTemplate("some-event").convertAndSend(exchangeSomeEvent, routingKeySomeEvent, message);
}

Assinante


Os assinantes também são muito simples. A única coisa que você precisa fazer é adicionar uma anotação ao método RabbitListener(anotação Spring AMQP por padrão) e passar o nome containerFactory.
Como descobrir o nome containerFactory correto para um host virtual?

Você não precisa saber, basta passar o nome do evento e a biblioteca fará toda a mágica por você! Essa biblioteca também tem a capacidade de incluir novas tentativas e dlq, que são recomendadas.

Ligá-los é muito simples. Você precisa adicionar anotação ao método EnableRabbitRetryAndDlqe passar o nome da propriedade como argumento.

Nota : você também pode especificar quais exceções tratar com tentativas e dlq. É processado por padrão Exception.class, o que significa lidar com todas as exceções.

Exemplo:

@RabbitListener(containerFactory = "some-event", queues = "${spring.rabbitmq.custom.some-event.queue}")
@EnableRabbitRetryAndDlq(event = "some-event", exceptions = { IllegalArgumentException.class, RuntimeException.class })
public void onMessage(Message message) {
   ...
}

É tudo! Espero que você goste desta biblioteca tanto quanto nós!

E, o mais importante: fique à vontade para contribuir ou enviar feedback!

Agradecimentos especiais para


André Luis Gomes
Rogerio Bueno
Leonardo Ferreira



Inscreva-se para uma aula gratuita.


All Articles