Итак, друзья! Мы с вами практически на финишной прямой нашего курса. Сегодня мы будем говорить о том, как довести наше микросервисное приложение от состояния "оно вроде работает на моём компьютере" до "оно стабильно работает в продакшене и всё покрыто тестами". Почему это важно? Потому что мир технологий жесток: если ваш сервис упадёт в 3 часа ночи, вы, а не ваши пользователи, будете проклинать тот день, когда забыли протестировать и задеплоить всё как следует.
Процессы тестирования
Интеграционные тесты: проверяем связность сервисов
Интеграционные тесты позволяют убедиться, что отдельные компоненты системы (например, микросервисы) корректно взаимодействуют друг с другом. Мы не тестируем каждую деталь, но проверяем "магистральное движение данных" между сервисами.
Пример: протестируем REST API одного из наших микросервисов с использованием Spring Boot Test и MockMvc:
@SpringBootTest
@AutoConfigureMockMvc
public class UserServiceIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testGetUserById() throws Exception {
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("John Doe"));
}
}
Этот код создаёт тестовый контекст Spring и отправляет HTTP-запрос к эндпоинту нашего микросервиса. Если бы что-то пошло не так, например, микросервис не смог бы достать данные из базы, тест бы провалился.
Нагрузочные тесты: насколько это всё быстро?
Нагрузочные тесты помогают оценить, как ваша система ведёт себя под высоким трафиком. Например, вы можете использовать Apache JMeter или Gatling для симуляции запросов. Настройте сценарий, где 1000 пользователей одновременно отправляют запросы к API, и смотрите, где "узкие места".
Контрактное тестирование: всё ли микросервисы понимают друг о друге?
Когда ваши микросервисы общаются, важно, чтобы они договаривались о формате данных. Здесь на помощь приходит Pact. Этот инструмент позволяет протестировать взаимодействие, чтобы изменения в одном сервисе не ломали другой.
Пример использования Pact для продюсера:
@ExtendWith(PactConsumerTestExt.class)
class ConsumerPactTest {
@Pact(consumer = "UserConsumer", provider = "UserProvider")
public RequestResponsePact createPact(PactDslWithProvider builder) {
return builder
.given("User exists")
.uponReceiving("Request for user")
.path("/users/1")
.method("GET")
.willRespondWith()
.status(200)
.body("{ \"id\": 1, \"name\": \"John Doe\" }")
.toPact();
}
@Test
@PactTestFor(pactMethod = "createPact")
public void testPact(MockServer mockServer) {
String url = mockServer.getUrl() + "/users/1";
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
Assertions.assertEquals("{\"id\":1,\"name\":\"John Doe\"}", response);
}
}
Здесь мы говорим "если провайдер отправляет такой JSON, я гарантирую, что мой консъюмер это съест".
Тестирование асинхронности: обработка Kafka-сообщений
Когда у вас есть Kafka, то без тестов для продюсеров и консъюмеров не обойтись. Для этого можно использовать библиотеку Embedded Kafka, которая поднимает тестовый кластер прямо внутри вашего Java-приложения.
Пример теста консюмера:
@SpringBootTest
@EnableKafka
@EmbeddedKafka(partitions = 1, topics = "test-topic")
public class KafkaConsumerTest {
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
@Test
public void testKafkaConsumer() {
String message = "Hello Kafka!";
kafkaTemplate.send("test-topic", message);
// Подождём чуть-чуть, чтобы консъюмер получил сообщение
Awaitility.await()
.atMost(5, TimeUnit.SECONDS)
.untilAsserted(() -> {
// Проверяем, что сообщение обработано
assertTrue(myCustomCondition());
});
}
}
Подготовка к деплою
CI/CD пайплайн: автоматизация сборки и тестирования
После того как мы протестировали всё возможное, нужно подготовить автоматический процесс сборки и развертывания. Это поможет избежать ситуации, когда "тесты на локальном компьютере прошли, а на сервере всё упало".
Пример простого GitLab CI пайплайна
stages:
- build
- test
- deploy
build:
stage: build
script:
- ./gradlew build
artifacts:
paths:
- build/libs/*.jar
test:
stage: test
script:
- ./gradlew test
deploy:
stage: deploy
script:
- docker-compose up -d
only:
- main
Этот пайплайн:
- Сначала собирает наш Spring Boot проект в JAR-файл.
- Запускает тесты для проверки работоспособности.
- Разворачивает приложение в Docker.
Контейнеризация: Docker
Теперь, чтобы деплой был простым, каждое приложение будет упаковано в Docker-контейнер.
Dockerfile для микросервиса
FROM openjdk:17-jdk-slim
COPY build/libs/my-microservice.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
Docker-compose для развёртывания нескольких микросервисов
version: "3.8"
services:
user-service:
build:
context: ./user-service
ports:
- "8081:8080"
order-service:
build:
context: ./order-service
ports:
- "8082:8080"
kafka:
image: bitnami/kafka:latest
ports:
- "9092:9092"
Эта конфигурация поднимет два микросервиса и Kafka.
Деплой и мониторинг
Деплой в тестовое окружение
Перед продакшеном система должна пройти тесты в тестовом окружении. Убедитесь, что:
- Все микросервисы поднялись.
- Kafka принимает и обрабатывает сообщения.
- API Gateway маршрутизирует запросы правильно.
Мониторинг в продакшене
Настройте Spring Boot Actuator и подключите метрики к инструментам мониторинга, таким как Prometheus и Grafana. Пример настройки метрик:
application.yml
management:
endpoints:
web:
exposure:
include: "*"
Подключите Actuator к Prometheus, и вы сможете отслеживать нагрузку и состояние микросервисов на дашбордах Grafana.
Таким образом, мы протестировали наши микросервисы, автоматизировали их сборку и реализовали развёртывание с использованием современных CI/CD практик. Теперь наша система готова к бою!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ