Открыть все вопросы по Spring
В этой статье:
➤ Как могут взаимодействовать микросервисы
➤ Как использовать Docker в Spring
➤ Как использовать Docker Compose в Spring
➤ Как использовать Kubernetes в Spring
➤ Как использовать Elasticsearch в Spring
➤ Как использовать RabbitMQ в Spring
➤ Как использовать Kafka в Spring
➤ Что такое Eureka Service
➤ Как реализовать Spring Cloud Gateway
➤ Как реализовать перенаправление при помощи Spring Cloud Gateway
➤ Что такое Service Discovery
➤ Что такое Dockerfile и Docker Compose
➤ Какие есть инструменты для наблюдения за микросервисами
➤ Что такое Trace ID и Span ID
➤ Как могут взаимодействовать микросервисы
В микросервисной архитектуре микросервисы могут взаимодействовать друг с другом несколькими способами, такими как синхронные и асинхронные вызовы. Рассмотрим основные подходы к взаимодействию микросервисов:
Синхронные вызовы (HTTP/REST):
Это наиболее распространенный способ взаимодействия микросервисов. Один микросервис делает HTTP-запрос к другому микросервису. В Spring Boot это можно реализовать с помощью RestTemplate или WebClient.
Пример использования RestTemplate:
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate
@Service
class UserService {
private val restTemplate = RestTemplate()
fun getUserDetails(userId: Long): User {
val url = "http://order-service/orders/user/$userId"
val response = restTemplate.getForObject(url, User::class.java)
return response ?: throw RuntimeException("User not found")
}
}Пример использования WebClient:
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
@Service
class UserService {
private val webClient = WebClient.create("http://order-service")
fun getUserDetails(userId: Long): User {
return webClient.get()
.uri("/orders/user/$userId")
.retrieve()
.bodyToMono(User::class.java)
.block() ?: throw RuntimeException("User not found")
}
}Асинхронные вызовы (сообщения):
Микросервисы могут взаимодействовать асинхронно, обмениваясь сообщениями через брокеры сообщений, такие как RabbitMQ, Apache Kafka и другие.
Пример использования RabbitMQ:
import org.springframework.amqp.core.Queue
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class RabbitMQConfig {
@Bean
fun queue(): Queue {
return Queue("example.queue", false)
}
}import org.springframework.amqp.rabbit.core.RabbitTemplate
import org.springframework.stereotype.Component
@Component
class MessageSender(private val rabbitTemplate: RabbitTemplate) {
fun sendMessage(message: String) {
rabbitTemplate.convertAndSend("example.queue", message)
}
}import org.springframework.amqp.rabbit.annotation.RabbitListener
import org.springframework.stereotype.Component
@Component
class MessageReceiver {
@RabbitListener(queues = ["example.queue"])
fun receiveMessage(message: String) {
println("Received message: $message")
}
}Взаимодействие через базу данных:
Микросервисы могут взаимодействовать через общую базу данных, однако это не рекомендуется, так как нарушает принцип изоляции данных в микросервисной архитектуре. Лучше использовать базу данных только для хранения данных, а взаимодействие осуществлять через API.
Использование сервисов обнаружения и маршрутизации:
Eureka (обнаружение сервисов)
Пример конфигурации Eureka:
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.cloud.client.discovery.DiscoveryClient
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate
@Service
class UserService {
@Autowired
private lateinit var discoveryClient: DiscoveryClient
private val restTemplate = RestTemplate()
fun getUserDetails(userId: Long): User {
val instances = discoveryClient.getInstances("order-service")
val orderServiceUri = instances[0].uri
val url = "$orderServiceUri/orders/user/$userId"
val response = restTemplate.getForObject(url, User::class.java)
return response ?: throw RuntimeException("User not found")
}
}Spring Cloud Gateway (маршрутизация):
используется для маршрутизации запросов к соответствующим микросервисам.
Пример конфигурации Gateway:
Использование gRPC:
gRPC — это современный RPC (Remote Procedure Call) фреймворк, который использует Protocol Buffers и поддерживает асинхронные вызовы.
Пример использования gRPC (Протокол (user.proto)):
syntax = "proto3";
option java_package = "com.example.demo";
option java_multiple_files = true;
service UserService {
rpc GetUserDetails (UserRequest) returns (UserResponse);
}
message UserRequest {
int64 userId = 1;
}
message UserResponse {
int64 id = 1;
string username = 2;
string password = 3;
string role = 4;
}import io.grpc.stub.StreamObserver
import net.devh.boot.grpc.server.service.GrpcService
@GrpcService
class UserServiceImpl : UserServiceGrpc.UserServiceImplBase() {
override fun getUserDetails(request: UserRequest, responseObserver: StreamObserver<UserResponse>) {
val user = UserResponse.newBuilder()
.setId(request.userId)
.setUsername("testuser")
.setPassword("password")
.setRole("ROLE_USER")
.build()
responseObserver.onNext(user)
responseObserver.onCompleted()
}
}import net.devh.boot.grpc.client.inject.GrpcClient
import org.springframework.stereotype.Service
@Service
class UserServiceClient {
@GrpcClient("user-service")
private lateinit var userServiceStub: UserServiceGrpc.UserServiceBlockingStub
fun getUserDetails(userId: Long): UserResponse {
val request = UserRequest.newBuilder().setUserId(userId).build()
return userServiceStub.getUserDetails(request)
}
}➤ Что такое Dockerfile и Docker Compose
Dockerfile:
используется для создания индивидуальных Docker-образов. Это основной способ описания, как построить образ контейнера с нужными зависимостями и конфигурациями.
# Использование базового образа FROM openjdk:11-jre-slim # Установка рабочей директории WORKDIR /app # Копирование jar-файла в контейнер COPY target/myapp.jar /app/myapp.jar # Указание команды для запуска приложения CMD ["java", "-jar", "myapp.jar"]
Docker Compose:
используется для управления многоконтейнерными приложениями. Это инструмент для оркестрации, который позволяет запускать и управлять несколькими контейнерами как одним целым, определяя их взаимодействие и зависимости.
version: '3.8'
services:
web:
build: .
ports:
- "8080:8080"
depends_on:
- db
db:
image: postgres:13
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydatabase
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:Оба инструмента часто используются вместе:
сначала создается Dockerfile для каждого сервиса, затем Docker Compose файл для управления всеми этими сервисами в одном приложении.
➤ Как использовать Docker в Spring
Docker — это платформа для разработки, доставки и запуска приложений в контейнерах. Контейнеры позволяют упаковать приложение с его зависимостями и обеспечить его изоляцию и портативность.
Основные концепции Docker:
Контейнеры:
Изолированные окружения, в которых запускаются приложения.
Образы:
Шаблоны, используемые для создания контейнеров.
Dockerfile:
Скрипт, описывающий, как создавать Docker-образ.
Docker Hub:
Облачный реестр, где можно хранить и делиться Docker-образами.
Основные команды Docker:
docker build:
Создание Docker-образа из Dockerfile.
docker run:
Запуск контейнера из Docker-образа.
docker pull:
Загрузка Docker-образа из реестра.
docker push:
Загрузка Docker-образа в реестр.
docker ps:
Список запущенных контейнеров.
docker stop/start:
Остановка/запуск контейнера.
# Этап 1: Сборка приложения FROM openjdk:21-jdk-slim AS build WORKDIR /app # Копируем файлы сборки Gradle и исходный код COPY build.gradle settings.gradle gradlew gradlew.bat ./ COPY gradle gradle COPY src src # Устанавливаем разрешения на выполнение файла gradlew RUN chmod +x ./gradlew # Скачиваем зависимости и собираем проект RUN ./gradlew bootJar # Этап 2: Создание минимального образа для запуска приложения FROM openjdk:21-jdk-slim WORKDIR /app # Копируем собранный jar-файл из этапа сборки COPY --from=build /app/build/libs/*.jar /app/app.jar # Указываем команду для запуска Spring Boot приложения ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Сборка проекта:
Соберите проект с помощью Maven или Gradle, чтобы получить исполняемый jar-файл в папке target.
./gradlew build
Сборка Docker-образа:
Используйте команду docker build, чтобы создать Docker-образ из Dockerfile.
docker build -t your-dockerhub-username/demo .
Запуск контейнера:
Запустите контейнер с помощью команды docker run.
docker run -p 8080:8080 your-dockerhub-username/demo
Теперь ваше приложение доступно по адресу http://localhost:8080.
Загрузка образа в Docker Hub:
Войдите в Docker Hub и загрузите ваш образ.
docker login docker push your-dockerhub-username/demo
Полезные команды Docker:
docker images:
Список всех образов.
docker ps -a:
Список всех контейнеров.
docker stop:
Остановка контейнера.
docker start:
Запуск остановленного контейнера.
docker rm:
Удаление контейнера.
docker rmi :
Удаление образа.
➤ Как использовать Docker Compose в Spring
Docker Compose — это инструмент для определения и управления многоконтейнерными Docker-приложениями. С помощью Docker Compose вы можете описать услуги, сети и тома в одном файле YAML, а затем легко развернуть их с помощью одной команды.
Пример использования Docker Compose:
Допустим, у нас есть Spring Boot приложение, которое использует базу данных PostgreSQL. Мы хотим развернуть эти два сервиса вместе с помощью Docker Compose.
spring.datasource.url=jdbc:postgresql://db:5432/mydatabase spring.datasource.username=postgres spring.datasource.password=postgres spring.jpa.hibernate.ddl-auto=update
Создание Dockerfile:
# Use the official image as a parent image FROM openjdk:11-jre-slim # Set the working directory in the container WORKDIR /app # Copy the jar file to the container COPY target/demo-0.0.1-SNAPSHOT.jar app.jar # Run the jar file ENTRYPOINT ["java", "-jar", "app.jar"]
Создайте файл docker-compose.yml в корневой директории проекта:
version: '3.8' # Указывает версию формата файла Docker Compose
services: # Определяет список сервисов, которые будут развернуты
app: # Определение сервиса для Spring Boot приложения
image: your-dockerhub-username/demo # Имя Docker-образа
build: # Определение процесса сборки Docker-образа
context: . # Контекст сборки, текущая директория
dockerfile: Dockerfile # Имя Dockerfile, который будет использоваться для сборки
ports:
- "8080:8080" # Проброс порта 8080 хоста на порт 8080 контейнера
environment: # Переменные окружения для настройки приложения
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/mydatabase # URL подключения к базе данных
SPRING_DATASOURCE_USERNAME: postgres # Имя пользователя для базы данных
SPRING_DATASOURCE_PASSWORD: postgres # Пароль для базы данных
depends_on: # Определяет зависимости от других сервисов
- db # Зависит от сервиса db
db: # Определение сервиса для базы данных PostgreSQL
image: postgres:13 # Имя Docker-образа PostgreSQL версии 13
environment: # Переменные окружения для настройки PostgreSQL
POSTGRES_DB: mydatabase # Имя базы данных, которая будет создана
POSTGRES_USER: postgres # Имя пользователя базы данных
POSTGRES_PASSWORD: postgres # Пароль для пользователя базы данных
ports:
- "5432:5432" # Проброс порта 5432 хоста на порт 5432 контейнера
volumes:
- postgres_data:/var/lib/postgresql/data # Создание тома для хранения данных PostgreSQL
volumes: # Определяет список томов, которые будут использоваться сервисами
postgres_data: # Том для хранения данных PostgreSQL, чтобы они сохранялись между перезапусками контейнераПояснение к каждой секции:
version:
Указывает версию формата файла Docker Compose. В данном случае используется версия 3.8.
services:
Определяет все сервисы, которые будут развернуты.
app:
Сервис для Spring Boot приложения.
image:
Имя Docker-образа, который будет использоваться для запуска контейнера.
build:
Определяет параметры сборки Docker-образа.
context:
Директория, в которой находится Dockerfile.
dockerfile:
Имя файла Dockerfile.
ports:
Проброс порта 8080 хоста на порт 8080 контейнера.
environment:
Переменные окружения для настройки подключения к базе данных.
depends_on:
Определяет, что этот сервис зависит от сервиса db.
db:
Сервис для базы данных PostgreSQL.
image:
Имя Docker-образа PostgreSQL версии 13.
environment:
Переменные окружения для настройки базы данных.
ports:
Проброс порта 5432 хоста на порт 5432 контейнера.
volumes:
Создание тома для хранения данных PostgreSQL.
volumes:
Определяет том, который будет использоваться для хранения данных PostgreSQL.
Как это работает:
Сборка и запуск:
При выполнении команды docker-compose up — build, Docker Compose сначала собирает Docker-образ для сервиса app с использованием указанного Dockerfile. Затем запускаются оба сервиса (app и db), причем сервис app зависит от сервиса db, и поэтому db запускается первым.
Переменные окружения:
Переменные окружения используются для настройки параметров подключения к базе данных в Spring Boot приложении.
Проброс портов:
Порты пробрасываются из контейнеров на хост-машину, чтобы можно было получить доступ к приложению и базе данных.
Том для данных:
Создается том postgres_data для хранения данных базы данных PostgreSQL, чтобы данные сохранялись между перезапусками контейнера.
Этот файл docker-compose.yml позволяет легко развернуть многоконтейнерное приложение с использованием Docker Compose, обеспечивая согласованность и удобство управления контейнерами.
Сборка и запуск:
Запустите docker-compose для сборки и запуска всех сервисов.
docker-compose up --build
Эта команда:
Соберет Docker-образ для вашего Spring Boot приложения.
Запустит контейнеры для приложения и базы данных PostgreSQL.
Настроит связь между контейнерами.
➤ Как использовать Kubernetes в Spring
Kubernetes — это система оркестрации контейнеров с открытым исходным кодом, которая позволяет автоматизировать развертывание, масштабирование и управление контейнеризованными приложениями. Она обеспечивает платформу для запуска контейнеров, их управления и масштабирования.
Вот пошаговое руководство по использованию Kubernetes для развертывания Spring Boot приложения.
Предварительные требования:
Docker:
Убедитесь, что Docker установлен и настроен.
Kubernetes:
Установите и настройте Kubernetes (например, с использованием Minikube для локальной разработки).
kubectl:
Установите утилиту командной строки kubectl для взаимодействия с кластером Kubernetes.
Создание Docker-образа:
# Use the official image as a parent image FROM openjdk:11-jre-slim # Set the working directory in the container WORKDIR /app # Copy the jar file to the container COPY target/demo-0.0.1-SNAPSHOT.jar app.jar # Run the jar file ENTRYPOINT ["java", "-jar", "app.jar"]
Соберите Docker-образ и загрузите его в Docker Hub (или любой другой реестр Docker-образов).
# Сборка Docker-образа docker build -t your-dockerhub-username/demo . # Вход в Docker Hub docker login # Загрузка Docker-образа в Docker Hub docker push your-dockerhub-username/demo
Создание Kubernetes манифестов:
Создайте манифесты Kubernetes для развертывания, службы и конфигурации Ingress.
deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
labels:
app: demo
spec:
replicas: 3
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: demo
image: your-dockerhub-username/demo:latest
ports:
- containerPort: 8080service.yaml:
apiVersion: v1
kind: Service
metadata:
name: demo-service
spec:
selector:
app: demo
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalanceringress.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: demo.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-service
port:
number: 80Применение манифестов Kubernetes:
Используйте команду kubectl для применения манифестов в вашем кластере Kubernetes.
kubectl apply -f deployment.yaml kubectl apply -f service.yaml kubectl apply -f ingress.yaml
Настройка DNS:
Для локальной разработки с Minikube добавьте следующую запись в файл /etc/hosts:
<MINIKUBE_IP> demo.local
Получите IP Minikube с помощью команды:
minikube ip
Проверка развертывания:
Проверьте состояние вашего развертывания, службы и ingress с помощью команд:
kubectl get deployments kubectl get services kubectl get ingress
Откройте браузер и перейдите по адресу http://demo.local, чтобы увидеть ваше развернутое Spring Boot приложение.
➤ Как использовать Apache Kafka в Spring
Apache Kafka — это распределенная стриминговая платформа, которая используется для построения систем обработки данных в реальном времени. Kafka изначально была разработана в LinkedIn и открыта как проект с открытым исходным кодом в 2011 году. Она используется для публикации, хранения и обработки потоков записей в реальном времени.
Основные компоненты Kafka:
Producer:
Отправляет записи (сообщения) в топики Kafka.
Consumer:
Читает записи (сообщения) из топиков Kafka.
Broker:
Kafka-сервер, который принимает данные от продюсеров, хранит их и передает их консюмерам. Кластер Kafka состоит из одного или нескольких брокеров.
Topic:
Логическая категория или канал, куда продюсеры отправляют данные и откуда консюмеры их читают. Топик разбивается на партиции для обеспечения параллелизма и масштабируемости.
Partition:
Подразделение топика. Каждая партиция является упорядоченным и неизменяемым логом, куда продюсеры добавляют сообщения.
Zookeeper:
Система координации, используемая Kafka для управления метаданными кластера, отслеживания статуса брокеров и топиков.
Как работает Kafka:
Публикация сообщений:
Продукторы публикуют сообщения в определенные топики. Сообщения сохраняются в партициях топика в виде логов.
Хранение сообщений:
Сообщения хранятся в партициях топиков на диске и могут быть настроены для хранения на определенное время или до достижения определенного объема данных.
Чтение сообщений:
Консьюмеры подписываются на топики и читают сообщения из партиций. Kafka позволяет консьюмерам контролировать, с какого места они начинают читать сообщения, что обеспечивает высокую гибкость и возможность повторного чтения данных.
Масштабируемость:
Партиции позволяют распределять нагрузку между несколькими брокерами, обеспечивая горизонтальное масштабирование.
Фоллтолерантность:
Репликация партиций между несколькими брокерами обеспечивает высокую доступность и устойчивость к сбоям.
Настройка Kafka:
Установите и настройте Apache Kafka и Zookeeper, используя Docker Compose.
Создайте файл docker-compose.yml
version: '3.8'
services:
zookeeper:
image: bitnami/zookeeper:latest
container_name: zookeeper
ports:
- "2181:2181"
environment:
- ALLOW_ANONYMOUS_LOGIN=yes
kafka:
image: bitnami/kafka:latest
container_name: kafka
ports:
- "9092:9092"
environment:
- KAFKA_BROKER_ID=1
- KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
- KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092
- ALLOW_PLAINTEXT_LISTENER=yesперейдите в командной строке в директорию. файла и запустите:
docker-compose up -d
Проверьте состояние
docker-compose ps
Создание производителя событий:
Создайте Spring Boot приложение с зависимостями Kafka.
dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.kafka:spring-kafka")
}spring.kafka.bootstrap-servers=localhost:9092 spring.kafka.consumer.group-id=my-group spring.kafka.consumer.auto-offset-reset=earliest spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
или через класс конфигурации
import org.apache.kafka.clients.producer.ProducerConfig
import org.apache.kafka.common.serialization.StringSerializer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.kafka.core.DefaultKafkaProducerFactory
import org.springframework.kafka.core.KafkaTemplate
import org.springframework.kafka.core.ProducerFactory
import org.springframework.kafka.support.serializer.JsonSerializer
@Configuration
class KafkaProducerConfig {
@Bean
fun producerFactory(): ProducerFactory<String, String> {
val configProps = HashMap<String, Any>()
configProps[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = "localhost:9092"
configProps[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java
configProps[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = JsonSerializer::class.java
return DefaultKafkaProducerFactory(configProps)
}
@Bean
fun kafkaTemplate(): KafkaTemplate<String, String> {
return KafkaTemplate(producerFactory())
}
}import org.apache.kafka.clients.consumer.ConsumerConfig
import org.apache.kafka.common.serialization.StringDeserializer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.kafka.annotation.EnableKafka
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory
import org.springframework.kafka.core.ConsumerFactory
import org.springframework.kafka.core.DefaultKafkaConsumerFactory
import org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
import org.springframework.kafka.support.serializer.JsonDeserializer
@EnableKafka
@Configuration
class KafkaConsumerConfig {
@Bean
fun consumerFactory(): ConsumerFactory<String, String> {
val configProps = HashMap<String, Any>()
configProps[ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG] = "localhost:9092"
configProps[ConsumerConfig.GROUP_ID_CONFIG] = "group_id"
configProps[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = ErrorHandlingDeserializer::class.java
configProps[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = ErrorHandlingDeserializer::class.java
configProps[ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS] = JsonDeserializer::class.java.name
return DefaultKafkaConsumerFactory(configProps, StringDeserializer(), JsonDeserializer())
}
@Bean
fun kafkaListenerContainerFactory(): ConcurrentKafkaListenerContainerFactory<String, String> {
val factory = ConcurrentKafkaListenerContainerFactory<String, String>()
factory.consumerFactory = consumerFactory()
return factory
}
}Producer и Consumer:
import org.springframework.kafka.core.KafkaTemplate
import org.springframework.stereotype.Service
@Service
class EventProducer(private val kafkaTemplate: KafkaTemplate<String, String>) {
fun sendMessage(topic: String, message: String) {
kafkaTemplate.send(topic, message)
}
}import org.springframework.kafka.annotation.KafkaListener
import org.springframework.stereotype.Service
@Service
class EventConsumer {
@KafkaListener(topics = ["topic_name"], groupId = "group_id")
fun consume(message: String) {
println("Consumed message: $message")
}
}➤ Как использовать RabbitMQ в Spring
RabbitMQ — это программное обеспечение для очередей сообщений (message broker), которое позволяет приложениям обмениваться сообщениями и выполнять задачи асинхронно. Оно поддерживает несколько протоколов обмена сообщениями и широко используется для создания распределенных и масштабируемых систем.
Основные концепции RabbitMQ:
Протокол AMQP:
RabbitMQ поддерживает протокол AMQP (Advanced Message Queuing Protocol), который определяет правила обмена сообщениями между клиентами и брокерами.
Очереди (Queues):
Очередь представляет собой буфер для хранения сообщений. Сообщения отправляются в очередь, где они ждут обработки получателем.
Обменники (Exchanges):
Обменник получает сообщения от производителя и маршрутизирует их в одну или несколько очередей в зависимости от установленных правил.
Привязки (Bindings):
Привязка связывает очередь с обменником и определяет правила маршрутизации сообщений из обменника в очередь.
Сообщения (Messages):
Сообщения представляют собой данные, которые передаются между приложениями через очереди. Каждое сообщение состоит из заголовка и тела (полезной нагрузки).
Производители (Producers):
Производители отправляют сообщения в обменники.
Потребители (Consumers):
Потребители получают сообщения из очередей и обрабатывают их.
Пример работы RabbitMQ:
Производитель (Producer) отправляет сообщение в обменник (Exchange).
Обменник маршрутизирует сообщение в соответствующую очередь (Queue) на основе установленных правил.
Потребитель (Consumer) получает сообщение из очереди и обрабатывает его.
Чтобы использовать RabbitMQ в Kotlin с использованием Spring Boot, вы можете воспользоваться библиотекой Spring AMQP (Spring for RabbitMQ)
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-amqp'
implementation 'org.jetbrains.kotlin:kotlin-reflect'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
}import org.springframework.amqp.core.Queue
import org.springframework.amqp.rabbit.connection.ConnectionFactory
import org.springframework.amqp.rabbit.core.RabbitTemplate
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class RabbitConfig {
@Bean
fun queue(): Queue {
return Queue("myQueue", false)
}
@Bean
fun rabbitTemplate(connectionFactory: ConnectionFactory): RabbitTemplate {
return RabbitTemplate(connectionFactory)
}
}import org.springframework.amqp.rabbit.core.RabbitTemplate
import org.springframework.stereotype.Service
@Service
class MessageSender(private val rabbitTemplate: RabbitTemplate) {
fun sendMessage(message: String) {
rabbitTemplate.convertAndSend("myQueue", message)
}
}import org.springframework.amqp.rabbit.annotation.RabbitListener
import org.springframework.stereotype.Service
@Service
class MessageListener {
@RabbitListener(queues = ["myQueue"])
fun receiveMessage(message: String) {
println("Received message: $message")
}
}import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
class MessageController(private val messageSender: MessageSender) {
// http://localhost:8080/send?message=Hello
@GetMapping("/send")
fun send(@RequestParam message: String) {
messageSender.sendMessage(message)
return "Message sent: $message"
}
}➤ Чем Kafka отличается от RabbitMQ
Kafka и RabbitMQ — это две популярные системы обмена сообщениями, которые используются для передачи сообщений между компонентами распределенных систем. Однако они имеют разные архитектурные подходы, целевые сценарии использования и особенности.
Apache Kafka:
Архитектура:
Журнал публикации-подписки (log-based pub/sub):
Kafka сохраняет сообщения в виде логов, что позволяет подписчикам читать сообщения с определенного смещения.
Брокер сообщений:
Kafka состоит из кластеров брокеров, которые хранят данные распределенно.
Хранение сообщений:
Сообщения хранятся на диске, и каждое сообщение имеет уникальное смещение. Это позволяет повторно читать сообщения и перематывать журнал.
Поддержка долговременного хранения данных.
Производительность:
Высокая производительность и пропускная способность, подходящая для обработки больших объемов данных в реальном времени.
Поддержка масштабирования:
Легко масштабируется горизонтально за счет добавления новых брокеров и разделов (partitions).
Целевые сценарии использования:
Потоковая обработка данных (stream processing).
Реализация событийных систем.
Аналитика данных в реальном времени.
Экосистема:
Включает такие компоненты, как Kafka Streams для обработки потоков и Kafka Connect для интеграции с различными источниками данных.
RabbitMQ:
Архитектура:
Очереди сообщений:
RabbitMQ использует концепцию очередей сообщений и маршрутизации (routing).
Брокер сообщений:
Сообщения передаются через обменники (exchanges) и очереди (queues).
Хранение сообщений:
Сообщения могут храниться в оперативной памяти или на диске.
Поддерживает надежную доставку сообщений через подтверждения (acknowledgments) и повторную попытку (retry).
Производительность:
Хорошая производительность для широкого спектра сценариев, но может быть менее эффективной при обработке больших объемов данных по сравнению с Kafka.
Поддержка масштабирования:
Поддержка кластеризации и федерации для горизонтального масштабирования, но может быть сложнее в настройке и управлении по сравнению с Kafka.
Целевые сценарии использования:
Классическая очередь задач (task queue) для асинхронной обработки.
Реализация системы обмена сообщениями с сложной маршрутизацией.
Интеграция и обмен данными между разнородными системами.
Экосистема:
Поддержка различных протоколов (AMQP, MQTT, STOMP).
Большое количество плагинов для расширения функциональности.
Kafka и RabbitMQ решают различные задачи в области передачи сообщений и имеют свои преимущества и ограничения. Kafka подходит для обработки больших объемов данных и потоковой обработки, в то время как RabbitMQ лучше справляется с задачами асинхронной обработки и сложной маршрутизацией сообщений. Выбор между ними зависит от конкретных требований и сценариев использования в вашем проекте.
➤ Что такое Eureka Service
Eureka Service — это часть набора инструментов для разработки микросервисов от Netflix, который называется Netflix OSS. Он используется для регистрации и обнаружения сервисов в распределенных системах. Eureka Service является центральной частью сервиса обнаружения (service discovery), который позволяет различным микросервисам находить и взаимодействовать друг с другом.
Основные компоненты Eureka:
Eureka Server:
Центральный сервер, который действует как регистратор для всех микросервисов. Микросервисы регистрируются в Eureka Server и сообщают о своем состоянии (healthy, down, etc.).
Eureka Client:
Клиенты Eureka, которые регистрируют себя на Eureka Server и могут использовать его для обнаружения других сервисов.
eureka-service-project ├── eureka-server │ ├── build.gradle │ ├── src │ │ ├── main │ │ │ ├── java (или kotlin) │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── eurekaserver │ │ │ │ └── EurekaServerApplication.kt │ │ │ ├── resources │ │ │ │ └── application.properties │ │ └── test │ │ ├── java (или kotlin) │ │ │ └── com │ │ │ └── example │ │ │ └── eurekaserver │ │ │ └── EurekaServerApplicationTests.kt ├── eureka-client │ ├── build.gradle │ ├── src │ │ ├── main │ │ │ ├── java (или kotlin) │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── eurekaclient │ │ │ │ ├── EurekaClientApplication.kt │ │ │ │ └── ServiceInstanceRestController.kt │ │ │ ├── resources │ │ │ │ └── application.properties │ │ └── test │ │ ├── java (или kotlin) │ │ │ └── com │ │ │ └── example │ │ │ └── eurekaclient │ │ │ └── EurekaClientApplicationTests.kt └── settings.gradle
Как работает Eureka Service:
Регистрация сервисов:
Каждый микросервис, который является клиентом Eureka, регистрирует себя на Eureka Server при запуске, предоставляя свою информацию, такую как адрес, порт и идентификатор сервиса.
Обновление статуса:
Клиенты регулярно отправляют “пинги” (heartbeats) на Eureka Server для подтверждения, что они все еще активны.
Обнаружение сервисов:
Микросервисы могут использовать Eureka Client для получения списка всех доступных сервисов, зарегистрированных в Eureka Server, и для взаимодействия с ними.
Основные аннотации и их использование в Eureka:
@EnableEurekaServer:
Включает Eureka Server.
@EnableEurekaClient:
Включает Eureka Client для регистрации и обнаружения сервисов.
@EnableDiscoveryClient:
Включает общий механизм обнаружения сервисов (может использоваться с различными системами обнаружения, включая Eureka).
@LoadBalanced:
Маркирует RestTemplate или WebClient для использования клиентской балансировки нагрузки.
Пример использования Eureka Service:
В settings.gradle нужно добавить includeBuild все модули
Пример для Eureka Server:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}server.port=8080 eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.server.enable-self-preservation=false
server.port=8761:
Задает порт, на котором будет запущен Eureka Server. По умолчанию Eureka Server запускается на порту 8080.
eureka.client.register-with-eureka=false:
Указывает, что Eureka Server не должен регистрироваться сам в себе как клиент. Это параметр конфигурации клиента Eureka, который указывает, что этот сервер Eureka не будет регистрироваться в другом сервере Eureka.
eureka.client.fetch-registry=false:
Указывает, что Eureka Server не должен извлекать (fetch) регистр других сервисов. Это также параметр конфигурации клиента Eureka, который отключает попытки получения списка всех зарегистрированных сервисов.
eureka.server.enable-self-preservation=false:
Отключает режим самосохранения (self-preservation mode) на Eureka Server. Этот режим предназначен для защиты от потери регистраций сервисов, когда сервер не получает пингов от клиентов в течение длительного времени. Отключение этого режима позволяет немедленно удалять сервисы из реестра, если они не посылают пинги, что может быть полезно для тестирования, но не рекомендуется для продакшн среды.
@EnableEurekaServer:
Эта аннотация используется для включения сервера Eureka. Она указывается на основном классе приложения, чтобы отметить его как Eureka Server.
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer
class EurekaServerApplication
fun main(args: Array<String>) {
runApplication<EurekaServerApplication>(*args)
}Пример Eureka Client:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}server.port=0 eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
server.port=0:
Задает порт, на котором будет запущен Eureka Client. В данном случае, клиентское приложение будет работать на порту, заданеном автоматически.
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/:
Указывает URL для регистрации клиента на Eureka Server. Параметр defaultZone определяет адрес сервера Eureka, с которым будет взаимодействовать клиент для регистрации и обнаружения сервисов. В данном случае, клиент будет регистрироваться на Eureka Server, работающем на http://localhost:8761/eureka/.
Дополнительные параметры, которые могут использоваться в application.properties:
Для Eureka Server:
eureka.instance.hostname:
Устанавливает имя хоста для Eureka Server.
eureka.instance.prefer-ip-address:
Указывает, следует ли использовать IP-адрес вместо имени хоста для регистрации.
Для Eureka Client:
eureka.instance.hostname:
Устанавливает имя хоста для Eureka Client.
eureka.instance.prefer-ip-address:
Указывает, следует ли использовать IP-адрес вместо имени хоста для регистрации клиента.
eureka.client.initial-instance-info-replication-interval-seconds:
Задает интервал в секундах между повторениями отправки метаданных экземпляра в Eureka Server.
eureka.client.registry-fetch-interval-seconds:
Задает интервал в секундах между попытками клиента получить обновленный регистр сервисов от Eureka Server.
eureka.client.instance-info-replication-interval-seconds:
Задает интервал в секундах между отправками информации об экземпляре сервиса в Eureka Server.
@EnableEurekaClient:
Эта аннотация используется для включения клиента Eureka. Она указывается на основном классе приложения, чтобы отметить его как клиент Eureka. Клиент автоматически регистрируется на Eureka Server и может использовать его для обнаружения других сервисов.
@EnableDiscoveryClient:
Эта аннотация является более общей аннотацией для включения обнаружения сервисов. Она позволяет приложению использовать разные механизмы обнаружения сервисов, включая Eureka. Она используется аналогично аннотации @EnableEurekaClient.
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.cloud.netflix.eureka.EnableEurekaClient
@SpringBootApplication
@EnableEurekaClient
class EurekaClientApplication
fun main(args: Array<String>) {
runApplication<EurekaClientApplication>(*args)
}import org.springframework.cloud.client.discovery.DiscoveryClient
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class ServiceInstanceRestController(private val discoveryClient: DiscoveryClient) {
@GetMapping("/service-instances")
fun serviceInstances(): List<String> {
return discoveryClient.services
}
}@LoadBalanced:
Эта аннотация используется для маркировки RestTemplate или WebClient, чтобы он использовал клиентскую балансировку нагрузки, основанную на Ribbon (если используется). Это позволяет RestTemplate или WebClient делать HTTP-запросы к именованным сервисам, зарегистрированным в Eureka, с балансировкой нагрузки между экземплярами сервиса.
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.cloud.client.loadbalancer.LoadBalanced
import org.springframework.context.annotation.Bean
import org.springframework.web.client.RestTemplate
@SpringBootApplication
class LoadBalancedApplication {
@Bean
@LoadBalanced
fun restTemplate(): RestTemplate {
return RestTemplate()
}
}
fun main(args: Array<String>) {
runApplication<LoadBalancedApplication>(*args)
}➤ Как реализовать Spring Cloud Gateway
Spring Cloud Gateway — это современный шлюз API, построенный на основе Spring Framework 5, Spring Boot 2 и проекта Spring WebFlux. Он предоставляет мощные возможности для маршрутизации и управления API-трафиком в микросервисной архитектуре. Spring Cloud Gateway спроектирован для обеспечения легкой интеграции с другими компонентами Spring Cloud и для работы в асинхронной и реактивной среде.
Основные возможности Spring Cloud Gateway:
Маршрутизация запросов:
Spring Cloud Gateway может маршрутизировать запросы к разным микросервисам на основе различных критериев, таких как путь, заголовки, параметры запроса и многое другое.
Фильтры:
Позволяет применять фильтры к запросам и ответам. Фильтры могут изменять запросы, добавлять или изменять заголовки, выполнять аутентификацию и авторизацию, логирование и многое другое.
Балансировка нагрузки:
Встроенная поддержка балансировки нагрузки позволяет распределять запросы между несколькими экземплярами микросервисов.
Интеграция с Eureka:
Spring Cloud Gateway легко интегрируется с Eureka и другими системами обнаружения сервисов для динамической маршрутизации запросов к зарегистрированным сервисам.
Поддержка асинхронной обработки:
Построен на реактивном стеке Spring WebFlux, что позволяет обрабатывать большое количество параллельных запросов с высокой производительностью.
Безопасность:
Поддержка различных механизмов аутентификации и авторизации, включая OAuth2.
Пример использования Spring Cloud Gateway:
Создайте новый проект Spring Boot с зависимостями для Spring Cloud Gateway и Spring Boot Actuator.
eureka-service-project ├── eureka-server │ ├── build.gradle │ ├── src │ │ ├── main │ │ │ └── ... │ │ └── ... ├── eureka-client │ ├── build.gradle │ ├── src │ │ ├── main │ │ │ └── ... │ │ └── ... ├── api-gateway │ ├── build.gradle │ ├── src │ │ ├── main │ │ │ └── ... │ │ └── ... └── settings.gradle
Настройка settings.gradle
rootProject.name = 'eureka-service-project' include 'eureka-server', 'eureka-client', 'api-gateway'
api-gateway/build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:2021.0.1"
}
}api-gateway/src/main/resources/application.properties
server.port=8080 spring.cloud.gateway.discovery.locator.enabled=true spring.cloud.gateway.discovery.locator.lower-case-service-id=true eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
api-gateway/src/main/kotlin/com/example/apigateway/ApiGatewayApplication.kt
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient
class ApiGatewayApplication
fun main(args: Array<String>) {
runApplication<ApiGatewayApplication>(*args)
}Пояснения к настройкам:
server.port=8080:
API Gateway будет запускаться на порту 8080.
spring.cloud.gateway.discovery.locator.enabled=true:
Включает автоматическое обнаружение маршрутов для микросервисов, зарегистрированных в Eureka.
spring.cloud.gateway.discovery.locator.lower-case-service-id=true:
Приводит идентификаторы сервисов к нижнему регистру для упрощения маршрутизации.
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/:
URL-адрес Eureka Server для регистрации и обнаружения сервисов.
Маршрутизация запросов:
Spring Cloud Gateway автоматически создает маршруты для всех микросервисов, зарегистрированных в Eureka. Например, если у вас есть микросервис с именем eureka-client, запросы к API Gateway на /eureka-client/** будут перенаправляться на соответствующий микросервис.
Маршрутизация может быть настроена в конфигурационном файле или программно.
src/main/resources/application.properties
server.port=8080
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
# Определение маршрутов для eureka-client
spring.cloud.gateway.routes[0].id=eureka-client
spring.cloud.gateway.routes[0].uri=lb://eureka-client
spring.cloud.gateway.routes[0].predicates[0]=Path=/eureka-client/**
spring.cloud.gateway.routes[0].filters[0]=RewritePath=/eureka-client/(?<remaining>.*), /${remaining}Пояснения к конфигурации:
server.port=8080:
Указывает, что Spring Cloud Gateway будет запущен на порту 8080.
spring.cloud.gateway.discovery.locator.enabled=true:
Включает автоматическое обнаружение маршрутов для сервисов, зарегистрированных в Eureka.
spring.cloud.gateway.discovery.locator.lower-case-service-id=true:
Преобразует идентификаторы сервисов в нижний регистр для упрощения маршрутизации.
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/:
Указывает URL Eureka Server для регистрации и обнаружения сервисов.
routes:
Настройка маршрутов. В данном случае, запросы, начинающиеся с /eureka-client/**, будут перенаправлены на сервис eureka-client.
➤ Как реализовать перенаправление при помощи Spring Cloud Gateway
Предположим, у нас есть два микросервиса, зарегистрированные в Eureka: order-service и inventory-service.
Мы хотим настроить маршрутизацию в Spring Cloud Gateway таким образом, чтобы запросы, начинающиеся с /orders/, перенаправлялись на order-service, а запросы, начинающиеся с /inventory/, перенаправлялись на inventory-service.
server.port=8080
# Включаем автоматическое обнаружение маршрутов для сервисов, зарегистрированных в Eureka
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
# Указываем URL Eureka Server для регистрации и обнаружения сервисов
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
# Определение маршрутов для order-service
spring.cloud.gateway.routes[0].id=order-service
spring.cloud.gateway.routes[0].uri=lb://order-service
spring.cloud.gateway.routes[0].predicates[0]=Path=/orders/**
spring.cloud.gateway.routes[0].filters[0]=RewritePath=/orders/(?<remaining>.*), /${remaining}
# Определение маршрутов для inventory-service
spring.cloud.gateway.routes[1].id=inventory-service
spring.cloud.gateway.routes[1].uri=lb://inventory-service
spring.cloud.gateway.routes[1].predicates[0]=Path=/inventory/**
spring.cloud.gateway.routes[1].filters[0]=RewritePath=/inventory/(?<remaining>.*), /${remaining}Пояснение параметров:
server.port:
Указывает, что Spring Cloud Gateway будет запущен на порту 8080.
spring.cloud.gateway.discovery.locator.enabled:
Включает автоматическое обнаружение маршрутов для сервисов, зарегистрированных в Eureka.
spring.cloud.gateway.discovery.locator.lower-case-service-id:
Преобразует идентификаторы сервисов в нижний регистр для упрощения маршрутизации.
eureka.client.service-url.defaultZone:
Указывает URL Eureka Server для регистрации и обнаружения сервисов.
spring.cloud.gateway.routes:
routes: Секция для определения маршрутов в Spring Cloud Gateway.
Маршрут для order-service:
id:
Уникальный идентификатор маршрута. В данном случае, это order-service.
uri:
URI для перенаправления запросов. Префикс lb:// указывает на использование встроенной балансировки нагрузки (load balancer), и order-service — это имя сервиса, зарегистрированного в Eureka.
predicates:
Условия, которые должны быть выполнены для того, чтобы запрос был перенаправлен этим маршрутом.
Path=/orders/:
Условие пути. Все запросы, начинающиеся с /orders/**, будут соответствовать этому маршруту.
filters:
Список фильтров, которые будут применяться к запросам и ответам.
RewritePath=/orders/(?.*), /${remaining}:
Фильтр, переписывающий путь запроса. В данном случае, часть пути /orders/ будет удалена из запроса перед его отправкой в order-service. Остальная часть пути будет сохранена и передана дальше.
Маршрут для inventory-service:
id:
Уникальный идентификатор маршрута. В данном случае, это inventory-service.
uri:
URI для перенаправления запросов. Префикс lb:// указывает на использование встроенной балансировки нагрузки (load balancer), и inventory-service — это имя сервиса, зарегистрированного в Eureka.
predicates:
Условия, которые должны быть выполнены для того, чтобы запрос был перенаправлен этим маршрутом.
Path=/inventory/:
Условие пути. Все запросы, начинающиеся с /inventory/**, будут соответствовать этому маршруту.
filters:
Список фильтров, которые будут применяться к запросам и ответам.
RewritePath=/inventory/(?.*), /${remaining}:
Фильтр, переписывающий путь запроса. В данном случае, часть пути /inventory/ будет удалена из запроса перед его отправкой в inventory-service. Остальная часть пути будет сохранена и передана дальше.
➤ Что такое Service Discovery
Service Discovery (обнаружение сервисов) — это механизм, который позволяет микросервисам находить и взаимодействовать друг с другом в распределенной системе без необходимости явного указания сетевых адресов. В современных микросервисных архитектурах, где количество сервисов может быть очень большим, управление их адресами вручную становится сложным и неудобным. Service Discovery автоматизирует процесс нахождения сервисов и упрощает масштабирование и управление системой.
Основные компоненты Service Discovery:
Сервис-регистратор (Service Registry):
Сервис-регистратор — это центральное хранилище, где все доступные сервисы регистрируются с их метаданными, такими как IP-адреса, порты и состояния. Примеры сервис-регистраторов включают Consul, Eureka, Zookeeper и Etcd.
Клиенты сервисов (Service Clients):
Клиенты сервисов — это компоненты, которые взаимодействуют с сервис-регистратором для регистрации, обновления и поиска сервисов.
Механизмы проверки состояния (Health Checks):
Механизмы проверки состояния используются для мониторинга доступности и работоспособности сервисов. Сервисы регулярно отправляют информацию о своем состоянии в сервис-регистратор, чтобы поддерживать актуальность данных.
Виды Service Discovery:
Клиентское обнаружение (Client-Side Discovery):
В этом подходе клиентские приложения сами запрашивают сервис-регистратор для получения адресов доступных сервисов. Клиентское обнаружение обычно реализуется с помощью библиотек, интегрированных в клиентские приложения.
Пример:
Netflix Eureka
Consul
Серверное обнаружение (Server-Side Discovery):
В этом подходе клиентские приложения отправляют запросы на серверный прокси или шлюз (например, API Gateway), который взаимодействует с сервис-регистратором и перенаправляет запросы на нужные сервисы.
Пример:
AWS Elastic Load Balancer (ELB)
Kubernetes Service
➤ Какие есть инструменты для наблюдения за микросервисами
Prometheus:
Система мониторинга и алертинга с мощным языком запросов.
Grafana:
Платформа для визуализации метрик и создания дашбордов.
Elasticsearch, Logstash, Kibana (ELK Stack):
Решение для централизованного логирования и анализа данных.
Jaeger, Zipkin:
Инструменты для распределенной трассировки.
➤ Как использовать Elasticsearch в Spring
Elasticsearch — это распределенная поисковая и аналитическая система с открытым исходным кодом, разработанная для хранения, поиска и анализа больших объемов данных в реальном времени. Elasticsearch часто используется для полнотекстового поиска, логов и аналитики.
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-elasticsearch")
}spring.elasticsearch.uris=http://localhost:9200 spring.elasticsearch.username=elastic spring.elasticsearch.password=your_password
import org.springframework.data.annotation.Id
import org.springframework.data.elasticsearch.annotations.Document
import org.springframework.data.elasticsearch.annotations.Field
@Document(indexName = "products")
data class Product(
@Id
val id: String? = null,
val name: String,
val description: String,
val price: Double,
@Field(type = Date)
val date: String;
)import org.springframework.data.elasticsearch.repository.ElasticsearchRepository
import org.springframework.stereotype.Repository
@Repository
interface ProductRepository : ElasticsearchRepository<Product, String> {
fun findByName(name: String): List<Product>
}import org.springframework.stereotype.Service
@Service
class ProductService(private val productRepository: ProductRepository) {
fun findAllProducts(): List<Product> {
return productRepository.findAll().toList()
}
fun saveProduct(product: Product): Product {
return productRepository.save(product)
}
fun findProductByName(name: String): List<Product> {
return productRepository.findByName(name)
}
}import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/products")
class ProductController(private val productService: ProductService) {
@GetMapping
fun getAllProducts(): List<Product> {
return productService.findAllProducts()
}
@PostMapping
fun createProduct(@RequestBody product: Product): Product {
return productService.saveProduct(product)
}
@GetMapping("/search")
fun searchProductsByName(@RequestParam name: String): List<Product> {
return productService.findProductByName(name)
}
}➤ Что такое Trace ID и Span ID
Trace ID и Span ID — это ключевые компоненты распределенной трассировки, которые используются для мониторинга и отладки распределенных систем. Они помогают отслеживать и связывать запросы, проходящие через множество сервисов, что позволяет понять и диагностировать поведение системы.
Trace ID:
это уникальный идентификатор, который присваивается каждому запросу, проходящему через систему. Этот идентификатор остается неизменным на протяжении всего жизненного цикла запроса и используется для связывания всех операций, связанных с этим запросом.
Цель:
Trace ID позволяет объединить все спаны (части запроса) в один общий след, чтобы можно было видеть полный путь запроса через все микросервисы и компоненты системы.
Span ID:
это уникальный идентификатор для конкретной операции или части запроса. Каждый спан представляет собой отдельный этап или сегмент запроса, например, вызов метода или выполнение запроса к базе данных.
Цель:
Span ID позволяет идентифицировать и отслеживать отдельные операции внутри одного запроса, предоставляя детальную информацию о каждом шаге.
Пример использования Trace ID и Span ID:
Рассмотрим, как Trace ID и Span ID могут использоваться в Spring Boot приложении с помощью библиотеки Spring Cloud Sleuth, которая интегрируется с Zipkin для распределенной трассировки.
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.cloud:spring-cloud-starter-sleuth")
implementation("org.springframework.cloud:spring-cloud-starter-zipkin")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
dependencyManagement {
imports {
mavenBom("org.springframework.cloud:spring-cloud-dependencies:2020.0.4")
}
}spring.zipkin.base-url=http://localhost:9411 spring.sleuth.sampler.probability=1.0
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.client.RestTemplate
@RestController
class TraceController(
@Autowired val restTemplate: RestTemplate
) {
@GetMapping("/trace")
fun trace(): String {
val response = restTemplate.getForObject(
"http://localhost:8081/external",
String::class.java
)
return "Trace ID and Span ID example: $response"
}
@GetMapping("/external")
fun external(): String {
return "External service response"
}
}Запустите Zipkin с помощью Docker:
docker run -d -p 9411:9411 openzipkin/zipkin
Запустите ваше Spring Boot приложение, и оно будет автоматически генерировать Trace ID и Span ID для всех запросов.
Просмотр трассировки:
Перейдите на http://localhost:9411 для открытия интерфейса Zipkin.
Отправьте GET запрос на http://localhost:8080/trace.
Вернитесь в интерфейс Zipkin и найдите след, связанный с вашим запросом. Вы увидите Trace ID и Span ID, отображающие путь запроса через ваши сервисы.