Спрощення інтеграції сервісів за допомогою Spring Boot та RabbitMQ

Будування сервісів — це завдання, яке передбачає дуже важливу деталь: як ці сервіси будуть взаємодіяти один з одним. Цю інтеграцію можна здійснити двома основними способами: асинхронно або синхронно. Кожен з них має свої особливості та може впливати на продуктивність, масштабованість і надійність системи. Тому, добре розуміючи різниці між ними, можна вибрати найкраще рішення для кожного конкретного випадку.

Асинхронна інтеграція

У разі асинхронної інтеграції один сервіс надсилає повідомлення і продовжує виконання без очікування відповіді. Інший сервіс отримує це повідомлення, коли готовий його обробити. Це чудово, оскільки сервіси не блокують один одного, тобто один може продовжувати працювати безперервно, навіть якщо інший займається обробкою або тимчасово не працює.

Такий тип інтеграції має кілька значних переваг:

  • Надійність: Один сервіс не залежить від іншого для продовження роботи.
  • Масштабованість: Можна додавати більше споживачів (або процесорів), які працюють паралельно, прискорюючи обробку.

Синхронна інтеграція

У випадку синхронної інтеграції все відбувається так, ніби один сервіс дзвонить іншому і чекає відповіді. Цей тип комунікації підходить для ситуацій, коли потрібен негайний результат, наприклад, для отримання даних з бази даних або виконання важливої транзакції.

Але є й певні складнощі:

  • Вузькі місця: Якщо інший сервіс затримається, все зупиниться.
  • Ризик збоїв: Проблеми з підключенням або затримки можуть паралізувати всю систему.

Інструменти для обміну повідомленнями та асинхронної інтеграції

Щоб асинхронна інтеграція працювала належним чином, зазвичай використовують інструменти для обміну повідомленнями. Ці системи є своєрідною "розумною поштою", яка організовує повідомлення між сервісами. Відомий приклад — RabbitMQ, який надійний, легкий у налаштуванні та має безліч корисних функцій.

Ці інструменти використовують так званий broker, що виступає посередником між відправником і отримувачем повідомлень. Повідомлення зберігаються в чергах або темах, і сервіси, які зацікавлені, "слухають" ці черги. Як тільки нове повідомлення надходить, вони його обробляють і продовжують виконання. Це дозволяє створювати добре відокремлені системи, готові до високих навантажень.

RabbitMQ та протокол AMQP

RabbitMQ використовує протокол AMQP (Advanced Message Queuing Protocol), який можна вважати "універсальною мовою" для обміну повідомленнями. Він гарантує, що повідомлення доставляються коректно та надає низку корисних функцій, таких як:

  • Розумне маршрутизування: Можна налаштувати правила для направлення кожного повідомлення в потрібне місце.
  • Підтвердження доставки: Гарантується, що споживач обробив повідомлення перед його видаленням з черги.
  • Персистентність: Повідомлення зберігаються, навіть якщо система виходить з ладу.
  • Масштабованість: Можна мати кілька виробників і споживачів, які працюють разом.

pic

RabbitMQ, доставка повідомлень.

Практичний приклад: IoT сенсори в сільському господарстві

Щоб показати, як це все працює на практиці, уявімо систему сенсорів IoT на фермі. Ці сенсори збирають дані, такі як температура та вологість, і надсилають цю інформацію до центральної системи. Ця система обробляє дані та надає їх через API для запитів.

Щоб не робити текст занадто довгим, ми не будемо розглядати всю структуру проєкту. Зосередимося на класах, які безпосередньо виконують магію: ті, що генерують і відправляють дані, і ті, що споживають і зберігають цю інформацію.

Як працює система?

  1. Симулятор сенсорів: Генерує змодельовані дані і відправляє повідомлення до черги в RabbitMQ.
  2. Сервіс споживача: Спостерігає за чергою, обробляє повідомлення і зберігає дані в MongoDB.

Основні класи проєкту

Проєкт Симулятор

У симуляторі є два основних класи:

1.
1. RandomDataGenerator: Цей клас генерує випадкові дані (наприклад, температуру та вологість) кожні 5 секунд.
2. RabbitMQProducer: Отримує згенеровані дані та відправляє їх як повідомлення до черги в RabbitMQ.

package br.com.acbueno.hortolandia.producer;  

import java.time.LocalDateTime;  
import java.util.Random;  
import java.util.UUID;  
import org.springframework.scheduling.annotation.Scheduled;  
import org.springframework.stereotype.Component;  
import br.com.acbueno.hortolandia.model.SensorData;  

@Component  
public class RamdomDataGenerator {  

 private final RabbitMQProducer producer;  
 private final Random random = new Random();  

 public RamdomDataGenerator(RabbitMQProducer rabbitMQProducer) {  
 this.producer = rabbitMQProducer;  
 }  

 @Scheduled(fixedRate = 5000)  
 public void generateDataAndSendData() {  
 SensorData data = new SensorData();  
 data.setSensorId(UUID.randomUUID().toString());  
 data.setTemperature(20 + random.nextDouble() * 15);  
 data.setSoilHumidity(30 + random.nextDouble() * 40);  
 data.setTimestamp(LocalDateTime.now());  
 producer.sendMenssage(data);  
 }  

}
package br.com.acbueno.hortolandia.producer;  

import org.springframework.amqp.rabbit.core.RabbitTemplate;  
import org.springframework.stereotype.Service;  
import br.com.acbueno.hortolandia.config.RabbitMQConfig;  
import br.com.acbueno.hortolandia.model.SensorData;  
import lombok.extern.slf4j.Slf4j;  

@Service  
@Slf4j  
public class RabbitMQProducer {  

 private final RabbitTemplate rabbitTemplate;  

 public RabbitMQProducer(RabbitTemplate rabbitTemplate) {  
 this.rabbitTemplate = rabbitTemplate;  
 }  

 public void sendMenssage(SensorData sensorData) {  
 rabbitTemplate.convertAndSend(RabbitMQConfig.QUEUE_NAME, sensorData);  
 log.info("Sent Message: {}", sensorData);  
 }  

}

Ці два класи працюють разом, щоб безперервно забезпечувати систему даними.

Проєкт сервера

З боку сервера магія відбувається в класі:

  1. RabbitMQConsumer: Цей клас "слухає" чергу в RabbitMQ, отримує повідомлення, обробляє їх і зберігає дані в MongoDB.
package br.com.acbueno.novaodessa.consumer;  

import java.time.LocalDateTime;  
import org.springframework.amqp.rabbit.annotation.RabbitListener;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Component;  
import br.com.acbueno.novaodessa.model.SensorData;  
import br.com.acbueno.novaodessa.repository.SensorDataRepository;  
import lombok.extern.slf4j.Slf4j;  

@Component  
@Slf4j  
public class RabbitMQConsumer {  

 @Autowired  
 private SensorDataRepository repository;  

 @RabbitListener(queues = "sensor_data_queue")  
 public void consumerMessage(SensorData sensorData) {  
 sensorData.setTimestamp(LocalDateTime.now());  
 repository.save(sensorData);  
 log.info("Message received: {}", sensorData.toString());  
 }  

}

Повний проєкт доступний за посиланням: https://github.com/acbueno/spring-iot-message

Перекладено з: Descomplicando a Integração de Serviços com Spring Boot e RabbitMQ

Leave a Reply

Your email address will not be published. Required fields are marked *