دمج بسيط لـ RabbitMQ و Spring Boot

تم إعداد ترجمة للمقال قبل بدء الدورة التدريبية "Developer on the Spring Framework" .




تحية للجميع!

أود أن أشارككم مكتبة مفتوحة المصدر تسهل دمج RabbitMQ مع التطبيقات في Spring Boot. بالإضافة إلى ذلك ، تقدم هذه المكتبة مفهومًا جديدًا ومحسنًا لعمليات إعادة المحاولة (مقارنةً بنهج Spring AMQP القياسي).
يبسط؟ ولكن كيف؟


التكوين التلقائي


لنفترض أننا نريد تنفيذ التفاعل (على سبيل المثال ، باستخدام SSL) مع RabbitMQ باستخدام Spring AMQP. نحن بحاجة إلى إنشاء عدد قليل من الفاصوليا، مثل ConnectionFactory، RabbitAdmin، RabbitTemplateو AbstractRabbitListenerContainerFactory.

ولكن تخيل أن هناك العديد من المضيفين الظاهريين في RabbitMQ. بشكل افتراضي ، في Spring AMQP ، تحتاج إلى إنشاء مثل هذا النظام البيئي لكل مضيف افتراضي مع التكوين اليدوي RabbitTemplateو RabbitListener.



كما ترى ، كل هذا التكوين يستغرق وقتًا ويمكن أن يكون صداعًا.

وإدراكًا لهذه المشكلة ، تقوم المكتبة المقترحة تلقائيًا بتكوين كل هذه الحبوب لك. الشيء الوحيد الذي عليك القيام به هو تحديد التكوين application.properties، وسيحدث السحر!
واو ، ولكن كيف يبدو التكوين في application.properties؟

كل شيء بسيط للغاية. أولاً ، هناك بادئة للخصائص - هذا spring.rabbitmq.custom.

بعد ذلك ، يجب تحديد اسم الحدث. هذا جزء مهم جدًا ، نظرًا لأن جميع الإعدادات التي تجريها المكتبة تستند إلى هذا الاسم.

بعد اسم الحدث ، يمكنك تحديد الخاصية والقيمة. يتم وصف جميع الخصائص في الوثائق .

لذلك ، القالب على النحو التالي: فيما يلي مثال لتكوين لاتصالين مختلفين.

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}

كما ترون ، لم نكتب سطرًا واحدًا من التعليمات البرمجية !!!
جيد ، لكنك كنت تقول شيئًا عن استراتيجية إعادة التشغيل الجديدة ، أليس كذلك؟

نعم قال صديقي!

ولكن قبل شرح هذه الاستراتيجية الجديدة ، دعونا نلقي نظرة على السلوك الافتراضي لـ RabbitMQ و Spring AMQP.

بشكل افتراضي ، لا يوفر RabbitMQ معالجة إعادة المحاولة التي تسمح لك بالتحكم في دورة حياة الرسالة بالكامل.

على سبيل المثال ، قبل RabbitMQ 3.8 ، لم يكن لرأس الرسالة خاصية للتحكم في عدد مرات إعادة المحاولة.

السلوك الافتراضي لـ RabbitMQ:

  • إذا لم تكن قد حددت مدة البقاء (TTL ، مدى الحياة) ، فسيحاول RabbitMQ باستمرار وضع رسالتك في قائمة الانتظار.
  • إذا قمت بتعريف TTL ولكنك لم تحدد dlx ، فستتم إزالة الرسالة بعد ذلك من قائمة الانتظار وستفقدها.
  • إذا قمت بتعريف TTL و dlx ، فبعد TTL سيتم إرسال الرسالة إلى المبادل المحدد في dlx.


السلوك الافتراضي RabbitMQ

ولكن ماذا لو أردنا زيادة مدة البقاء (على سبيل المثال ، في حالة التشغيل غير المستقر) والتحكم في عدد مرات إعادة المحاولة؟

سؤال عظيم!

حان الوقت لشرح كيفية عمل مكتبة الربيع AQMP ومكتبة ضبط أرنب الربيع!

استراتيجيات إعادة المحاولة


الربيع AMQP بشكل افتراضي


عند استخدام Spring AMQP بشكل افتراضي ، يمكنك تحديد خيارات إعادة المحاولة باستخدام الخصائص أدناه.

لكن هذا النهج لديه مشاكل. بشكل افتراضي ، سيحظر Spring AMQP قائمة الانتظار الخاصة بك عندما تحاول تسليم رسالة مرة أخرى.

يمكن حل هذه المشكلة بطريقة مستديرة: استخدم التوازي. ولكن بهذه الطريقة نقوم بتحميل JVM ، وهذا ليس أفضل نهج. إذا كان لديك خمس رسائل إشكالية (كما في المثال) ، فسيظهر اختناق مرة أخرى ، وما زلنا بحاجة إلى تحديد الفول يدويًا في @Configurationالحاوية لكل اتصال وحاوية.

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


ضبط أرنب الربيع


تتبع هذه المكتبة نهجًا مختلفًا مع قائمة انتظار منفصلة لإعادة المحاولة. بهذه الطريقة يمكننا التحكم في TTL باستخدام معلمة رسالة انتهاء الصلاحية ، ومعلمة x-death للتحكم في عدد مرات إعادة المحاولة.
ولكن كيف؟

نستخدم مفهوم dlx في قائمة انتظار إعادة المحاولة لإعادة إرسال الرسالة إلى قائمة الانتظار الرئيسية. وبالتالي ، يمكننا الوصول إلى معلمة x-death ويمكننا تحديد عمر الرسالة برمجيًا.

ملحوظة. نظرًا لأن هذه المكتبة هي امتداد لـ Spring AMQP ، يمكنك استخدام إستراتيجية إعادة المحاولة الافتراضية ، واستخدام هذه المكتبة فقط للتكوين التلقائي للفاصوليا.


أرنب الربيع
هل يمكنني استخدام استراتيجية إعادة المحاولة الخاصة بك بدون التكوين التلقائي؟ لدي بالفعل الكثير من الفاصوليا ، وليس لدي الوقت لإعادة كتابتها.

نحن نعلم أن العالم ليس مثاليًا ، وفي هذه الحالة ، لدينا علامة تسمح لك بتعطيل التكوين التلقائي واستخدام نهجنا فقط لمعالجة التكرار والمزايا الأخرى ، مثل إدارة الحاوية.

يمكنك تعطيل التكوين التلقائي باستخدام الخاصية التالية:

spring.rabbitmq.enable.custom.autoconfiguration=false

ولكن لدي ارتباطين مختلفين ، فماذا أفعل في هذه الحالة؟

إذا كان لديك أكثر من اتصال وتريد تعطيل التكوين التلقائي ، فأنت بحاجة إلى تحديد اسم الحبة RabbitTemplateلكل اتصال. هذا موصوف هنا . لا يزال بإمكانك استخدام الفول لجعل العمل مع Spring AMQP أسهل.

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

RabbitTemplateHandler
ممتاز! لذا ، أريد استخدام هذا. كيف يمكنني أن أفعل هذا؟ هل لديك مثال أو وثائق؟
نعم!

لدينا مثال مشروع في هذا المستودع ، حيث يمكنك معرفة كيفية استخدام هذه المكتبة للناشرين والمشتركين.

لكن الأمر بسيط للغاية لدرجة أنني سأضرب مثالاً هنا!

الناشر


يوجد فصل في المكتبة RabbitTemplateHandlerوهو سهل الاستخدام للغاية. تحتاج إلى استدعاء الأسلوب getRabbitTemplateوتمرير المضيف الظاهري كمعلمة للحصول على الفول RabbitTemplate. بعد ذلك ، يمكنك استدعاء طريقة convertAndSend وتمريرها في المعلمات الرئيسية للتبادل والتوجيه.

ملاحظة : يمكن الحصول على مفتاح التبادل والتوجيه باستخدام التعليقات التوضيحيةالقيمة.

مثال :

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

مشترك


المشتركون ايضا بسيطون جدا الشيء الوحيد الذي عليك القيام به هو إضافة تعليق توضيحي إلى الأسلوب RabbitListener(التعليق التوضيحي لربيع AMQP افتراضيًا) وتمرير الاسم containerFactory.
كيفية معرفة الحاوية الصحيحة اسم المصنع لمضيف افتراضي؟

لا تحتاج إلى معرفته ، فقط قم بتمرير اسم الحدث ، وستقوم المكتبة بكل السحر! تحتوي هذه المكتبة أيضًا على القدرة على تضمين عمليات إعادة المحاولة و dlq الموصى بها.

تشغيلها بسيط للغاية. تحتاج إلى إضافة تعليق توضيحي إلى الطريقة EnableRabbitRetryAndDlqوتمرير اسم الخاصية كوسيطة.

ملاحظة : يمكنك أيضًا تحديد الاستثناءات للتعامل مع عمليات إعادة المحاولة و dlq. تتم معالجتها بشكل افتراضي Exception.class، مما يعني التعامل مع جميع الاستثناءات.

مثال:

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

كل شيء! آمل أن تستمتع بهذه المكتبة بقدر ما نتمتع!

والأهم من ذلك: لا تتردد في المساهمة أو إرسال التعليقات!

شكر خاص ل


أندريه لويس جوميز
روجريو بوينو
ليوناردو فيريرا



سجل للحصول على درس مجاني.


All Articles