Открыть все вопросы по 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: 8080
service.yaml:
apiVersion: v1 kind: Service metadata: name: demo-service spec: selector: app: demo ports: - protocol: TCP port: 80 targetPort: 8080 type: LoadBalancer
ingress.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, отображающие путь запроса через ваши сервисы.