Integración simple de RabbitMQ y Spring Boot

Se preparó una traducción del artículo antes del comienzo del curso "Desarrollador en el marco de Spring" .




¡Hola a todos!

Me gustaría compartir con ustedes una biblioteca de código abierto que facilita la integración de RabbitMQ con aplicaciones en Spring Boot. Además, esta biblioteca ofrece un concepto nuevo y mejorado de reintentos (en comparación con el enfoque estándar Spring AMQP).
Simplifica? ¿Pero cómo?


Autoconfiguración


Supongamos que queremos implementar la interacción (por ejemplo, usando SSL) con RabbitMQ usando Spring AMQP. Tenemos que crear unas cuantas judías, tales como ConnectionFactory, RabbitAdmin, RabbitTemplatey AbstractRabbitListenerContainerFactory.

Pero imagine que hay varios hosts virtuales en RabbitMQ. De forma predeterminada, en Spring AMQP, debe crear dicho ecosistema para cada host virtual con configuración manual de RabbitTemplatey RabbitListener.



Como puede ver, toda esta configuración lleva tiempo y puede ser un dolor de cabeza.

Consciente de este problema, la biblioteca propuesta configura automáticamente todos estos beans por usted. ¡Lo único que debe hacer es determinar la configuración application.propertiesy la magia ocurrirá!
Wow, pero ¿cómo se ve la configuración application.properties?

Todo es muy simple. En primer lugar, hay un prefijo para las propiedades: esto spring.rabbitmq.custom.

Después de eso, debe especificar el nombre del evento. Esta es una parte muy importante, ya que todas las configuraciones que realiza la biblioteca se basan en este nombre.

Después del nombre del evento, puede especificar la propiedad y el valor. Todas las propiedades se describen en la documentación .

Entonces, la plantilla es la siguiente: A continuación se muestra un ejemplo de configuración para dos conexiones 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 puede ver, ¡no hemos escrito una sola línea de código!
Bien, pero estabas diciendo algo sobre la nueva estrategia de repetición, ¿verdad?

Si mi amigo dijo!

Pero antes de explicar esta nueva estrategia, echemos un vistazo al comportamiento predeterminado de RabbitMQ y Spring AMQP.

Por defecto, RabbitMQ no proporciona un proceso de reintento que le permita controlar todo el ciclo de vida del mensaje.

Por ejemplo, antes de RabbitMQ 3.8 , el encabezado del mensaje no tenía una propiedad para controlar el número de reintentos realizados.

Comportamiento predeterminado de RabbitMQ:

  • Si no ha definido el tiempo de vida (TTL, duración), RabbitMQ intentará constantemente poner en cola su mensaje.
  • Si definió TTL pero no definió dlx , después de TTL el mensaje se eliminará de la cola y lo perderá.
  • Si ha definido TTL y dlx , luego de TTL el mensaje se enviará al intercambiador definido en dlx.


Comportamiento predeterminado de RabbitMQ

Pero, ¿qué sucede si queremos un TTL creciente (por ejemplo, en caso de operación inestable) y controlar el número de reintentos?

Gran pregunta!

¡Es hora de explicar cómo funcionan las bibliotecas Spring AQMP y Spring Rabbit Tuning!

Reintentar estrategias


Spring AMQP por defecto


Cuando usa Spring AMQP de manera predeterminada, puede definir las opciones de reintento usando las propiedades a continuación.

Pero este enfoque tiene problemas. De forma predeterminada, Spring AMQP bloqueará su cola cuando intente entregar un mensaje nuevamente.

Este problema se puede resolver de forma indirecta: utilice el paralelismo. Pero de esta manera cargamos la JVM, y este no es el mejor enfoque. Si tiene cinco mensajes problemáticos (como en el ejemplo), volverá a surgir un cuello de botella y aún debemos determinar manualmente los beans en el @Configurationcontenedor para cada conexión y contenedor.

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


Spring RabbitMQ Tuning


Esta biblioteca adopta un enfoque diferente con una cola separada para reintentos. De esta forma podemos controlar TTL utilizando el parámetro del mensaje de vencimiento y el parámetro x-death para controlar el número de reintentos.
¿Pero cómo?

Utilizamos el concepto dlx en la cola de reintentos para reenviar el mensaje a la cola principal. Por lo tanto, tenemos acceso al parámetro x-death y podemos determinar mediante programación la duración del mensaje.

Nota. Como esta biblioteca es una extensión de Spring AMQP, puede usar la estrategia de reintento predeterminada y usar esta biblioteca solo para la configuración automática de beans.


Spring RabbitMQ Tuning Retry
¿Puedo usar su estrategia de reintento sin configuración automática? Ya tengo muchos frijoles y no tengo tiempo para reescribirlos.

Sabemos que el mundo no es perfecto, y para esta situación, tenemos un indicador que le permite desactivar la configuración automática y usar solo nuestro enfoque para el procesamiento de repetición y otras ventajas, como la administración de contenedores.

Puede deshabilitar la configuración automática mediante la siguiente propiedad:

spring.rabbitmq.enable.custom.autoconfiguration=false

Pero tengo dos conexiones diferentes, ¿qué debo hacer en este caso?

Si tiene más de una conexión y desea deshabilitar la configuración automática, debe especificar el nombre del bean RabbitTemplatepara cada conexión. Esto se describe aquí . Todavía puede usar nuestro bean para facilitar el trabajo con Spring AMQP.

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

RabbitTemplateHandler
¡Muy bien! Entonces, quiero usar esto. ¿Cómo puedo hacer esto? ¿Tienes un ejemplo o documentación?
¡Si!

Tenemos un proyecto de ejemplo en este repositorio , donde puede ver cómo usar esta biblioteca para editores y suscriptores.

¡Pero es tan simple que daré un ejemplo aquí!

Editor


Hay una clase en la biblioteca RabbitTemplateHandlery es muy fácil de usar. Debe llamar al método getRabbitTemplatey pasar el host virtual como parámetro para obtener el bean RabbitTemplate. Después de eso, puede llamar al método convertAndSend y pasarlo en los parámetros clave de intercambio y enrutamiento.

Nota : la clave de intercambio y enrutamiento se puede obtener mediante anotacionesValor.

Un ejemplo :

@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);
}

Abonado


Los suscriptores también son muy simples. Lo único que debe hacer es agregar una anotación al método RabbitListener(anotación Spring AMQP por defecto) y pasar el nombre containerFactory.
¿Cómo encontrar el nombre correcto de containerFactory para un host virtual?

¡No necesita saberlo, simplemente pase el nombre del evento y la biblioteca hará toda la magia por usted! Esta biblioteca también tiene la capacidad de incluir reintentos y dlq, que se recomiendan.

Encenderlos es muy simple. Debe agregar una anotación al método EnableRabbitRetryAndDlqy pasar el nombre de la propiedad como argumento.

Nota : también puede especificar qué excepciones manejar con reintentos y dlq. Se procesa de manera predeterminada Exception.class, lo que significa manejar todas las excepciones.

Ejemplo:

@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) {
   ...
}

Es todo! ¡Espero que disfrutes de esta biblioteca tanto como nosotros!

Y, lo más importante: ¡siéntase libre de contribuir o enviar comentarios!

Agradecimientos especiales a


Andre Luis Gomes
Rogerio Bueno
Leonardo Ferreira



Regístrese para una lección gratis.


All Articles