Архітектура мікросервісів: Проектування масштабованих Java-додатків за допомогою Spring

pic

Я ділюсь своїми проектами на GitHub (@zeglami) — будь ласка, підпишись, щоб підтримати мене 😃

_Стаття була написана на основі мого старого репозиторію [spring-microservice_](https://github.com/zeglami/micro-service-spring)

1- Вступ

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

1.1 — Слабко зв'язаний vs.

Тісно зв'язані системи

pic

1.2 — Слабко зв'язані

// Приклад слабко зв'язаних систем  

// Інтерфейс повідомлення  
interface NotificationService {  
 void sendNotification(String message);  
}  

// Реалізація служби електронної пошти  
class EmailService implements NotificationService {  
 public void sendNotification(String message) {  
 System.out.println("Відправляється email: " + message);  
 }  
}  

// Реалізація служби SMS  
class SMSService implements NotificationService {  
 public void sendNotification(String message) {  
 System.out.println("Відправляється SMS: " + message);  
 }  
}  

// Служба користувачів, яка залежить від інтерфейсу повідомлень  
class UserService {  
 private NotificationService notificationService;  

 public UserService(NotificationService notificationService) {  
 this.notificationService = notificationService; // Впровадження залежності  
 }  

 public void notifyUser(String message) {  
 notificationService.sendNotification(message);  
 }  
}  

public class Main {  
 public static void main(String[] args) {  
 NotificationService emailService = new EmailService();  
 UserService userService = new UserService(emailService);  
 userService.notifyUser("Ласкаво просимо до нашої служби!");  

 // Перехід до SMS служби  
 NotificationService smsService = new SMSService();  
 UserService userServiceSMS = new UserService(smsService);  
 userServiceSMS.notifyUser("Ласкаво просимо до нашої служби!");  
 }  
}

Характеристики

Впровадження залежності: UserService приймає інтерфейс NotificationService як параметр конструктора, що дозволяє використовувати різні реалізації.

Легкість змін: Щоб змінити метод повідомлень, можна просто передати іншу реалізацію (наприклад, SMSService), не змінюючи UserService.

Полегшене тестування: UserService можна тестувати ізольовано, передавши мок-реалізацію NotificationService.

1.3 — Тісно зв'язаний

// Приклад тісно зв'язаних систем  

class EmailService {  
 public void sendEmail(String message) {  
 System.out.println("Відправляється email: " + message);  
 }  
}  

class NotificationService {  
 private EmailService emailService = new EmailService(); // Пряма залежність  

 public void notifyUser(String message) {  
 emailService.sendEmail(message);  
 }  
}  

public class Main {  
 public static void main(String[] args) {  
 NotificationService notificationService = new NotificationService();  
 notificationService.notifyUser("Ласкаво просимо до нашої служби!");  
 }  
}

Характеристики

Пряма залежність: NotificationService безпосередньо створює екземпляр EmailService.

Складність змін: Якщо ми хочемо змінити метод повідомлень (наприклад, на SMS), потрібно змінювати клас NotificationService.

Проблеми з тестуванням: Тестування NotificationService ізольовано складне, оскільки він безпосередньо залежить від EmailService.

1.4 — Традиційний моноліт і мікросервіси

pic

2 — Огляд проекту та діаграма архітектури

pic

  • API Gateway (Spring Cloud Gateway) надає єдину точку входу.
  • Eureka Server виконує роль реєстру сервісів.
  • Служба клієнтів (керує інформацією про клієнтів).
  • Служба інвентаризації (керує продуктами на складі).
  • Служба виставлення рахунків (керує замовленнями та оплатами).

Кожен сервіс має власну базу даних, що дозволяє їм працювати та масштабуватися незалежно.

3 — Основні мікросервіси в прикладі додатку

Джерело коду проекту: https://github.com/zeglami/micro-service-spring

Зверніть увагу, що це лише приклад і може не відповідати найкращим практикам; основна мета — продемонструвати архітектуру мікросервісів.

3.1 — Служба клієнтів

Керує всіма операціями, що стосуються клієнтів, такими як реєстрація та управління профілем.

// Використовує Spring Data JPA для зберігання даних.

// Відкриває REST API через Spring Data REST.
package me.zegit.customerservice;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.config.Projection;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/*
https://fr.wikipedia.org/wiki/HATEOAS
За замовчуванням ми отримуємо формат HATEOAS

Щоб використовувати API:
http://localhost:8081/customers?page=0&size=2
http://localhost:8081/customers
http://localhost:8081/customers/1

Щоб отримати доступ до бази даних (див. application.properties):
http://localhost:8081/h2-console/
*/

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String phone;
private String address;
}

@RepositoryRestResource
interface CustomerRepository extends JpaRepository {
// Користувацький метод для пошуку за ім'ям (часткове співпадіння)
// Наприклад

// GET /customers/search/findByNameContains?kw=l
// із "l" поверне клієнтів з "l" в їхньому імені
java.util.List findByNameContains(String kw);
}

// http://localhost:8081/customers/2?projection=p1
@Projection(name = "p1", types = Customer.class)
interface CustomerProjection {
Long getId();
String getName();
}

// Нова проекція з більше полями
// http://localhost:8081/customers/1?projection=p2
@Projection(name = "p2", types = Customer.class)
interface CustomerProjection2 {
Long getId();
String getName();
String getEmail();
String getPhone();
String getAddress();
}

@SpringBootApplication
public class CustomerServiceApplication {

public static void main(String[] args) {
SpringApplication.run(CustomerServiceApplication.class, args);
}

@Bean
CommandLineRunner start(CustomerRepository customerRepository,
RepositoryRestConfiguration repositoryRestConfiguration) {
return args -> {
repositoryRestConfiguration.exposeIdsFor(Customer.class);
customerRepository.save(new Customer(null, "Abdel", "[email protected]", "111-111-1111", "123 Main St"));
customerRepository.save(new Customer(null, "Ben", "[email protected]", "222-222-2222", "456 Elm St"));
customerRepository.save(new Customer(null, "Antho", "[email protected]", "333-333-3333", "789 Oak Ave"));
customerRepository.save(new Customer(null, "Linda", "[email protected]", "444-444-4444", "321 Pine Rd"));
customerRepository.findAll().forEach(System.out::println);
};
}
}
```

### application.properties  

################################  
#База Даних  
################################  
spring.datasource.initialize=true  
spring.datasource.url=jdbc:h2:mem:inventory;DB_CLOSE_DELAY=- 1;DB_CLOSE_ON_EXIT=FALSE  
spring.datasource.driverClassName=org.h2.Driver  
spring.datasource.username=sa  
spring.datasource.password=  
spring.h2.console.enabled=true  

################################  
#Інше  
################################  
#spring.cloud.discovery.enabled=false за замовчуванням spring буде шукати eureka, щоб зареєструвати, тому встановіть false, якщо не хочете  
spring.cloud.discovery.enabled=true  
server.port=8081  
spring.application.name=customer-service  
#management.endpoints.web.exposure.include=*  

################################  
#Eureka  
################################  
eureka.client.service-url.defaultZone=http://localhost:8761/eureka

3.2 — Inventory Service

// Обробляє інформацію про продукти.  
// Підтримує каталог продуктів.


// Використовує базу даних H2 в пам'яті  
package me.zegit.inventoryservice;  

import lombok.AllArgsConstructor;  
import lombok.Data;  
import lombok.NoArgsConstructor;  
import lombok.ToString;  
import org.springframework.boot.CommandLineRunner;  
import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.context.annotation.Bean;  
import org.springframework.data.jpa.repository.JpaRepository;  
import org.springframework.data.rest.core.annotation.RepositoryRestResource;  
import org.springframework.data.rest.core.config.Projection;  
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;  

import javax.persistence.Entity;  
import javax.persistence.GeneratedValue;  
import javax.persistence.GenerationType;  
import javax.persistence.Id;  

/*  
Для тестування API (HATEOAS за замовчуванням):  
http://localhost:8082/products  
http://localhost:8082/products/1  

Для консолі бази даних (див. application.properties):  
http://localhost:8082/h2-console/  
 */  

@Entity  
@Data  
@NoArgsConstructor  
@AllArgsConstructor  
@ToString  
class Product {  
 @Id  
 @GeneratedValue(strategy = GenerationType.IDENTITY)  
 private Long id;  
 private String name;  
 private double price;  

 // Додаткові опційні поля (можете видалити, якщо не потрібні)  
 private String description;  
 private String category;  
}  

@RepositoryRestResource  
interface ProductRepository extends JpaRepository {  
 // Демонстрація власного методу для пошуку за підрядком у назві (необов'язково)  
 // напр. GET /products/search/findByNameContains?kw=ball  
 java.util.List findByNameContains(String kw);  
}  

// Приклад короткої проекції: p1  
// http://localhost:8082/products/1?projection=p1  
@Projection(name = "p1", types = Product.class)  
interface ProductProjectionShort {  
 Long getId();  
 String getName();  
 double getPrice();  
}  

// Приклад детальної проекції: p2  
// http://localhost:8082/products/1?projection=p2  
@Projection(name = "p2", types = Product.class)  
interface ProductProjectionDetailed {  
 Long getId();  
 String getName();  
 double getPrice();  
 String getDescription();  
 String getCategory();  
}  

@SpringBootApplication  
public class InventoryServiceApplication {  

 public static void main(String[] args) {  
 SpringApplication.run(InventoryServiceApplication.class, args);  
 }  

 @Bean  
 CommandLineRunner start(ProductRepository productRepository,  
 RepositoryRestConfiguration repositoryRestConfiguration) {  
 return args -> {  
 // Відкриває ідентифікатори продуктів у REST відповідях  
 repositoryRestConfiguration.exposeIdsFor(Product.class);  

 // Створює продукти, пов'язані з тенісом  
 productRepository.save(new Product(null, "Tennis ball", 2, "High-quality felt ball", "Tennis"));  
 productRepository.save(new Product(null, "Racket", 120, "Pro-level tennis racket", "Tennis"));  
 productRepository.save(new Product(null, "Tennis trainer", 25, "Solo training device", "Tennis"));  

 // Виводить усі збережені продукти в консолі  
 productRepository.findAll().forEach(System.out::println);  
 };  
 }  
}
### application.properties  

################################  
#База даних  
################################  
spring.datasource.initialize=true  
spring.datasource.url=jdbc:h2:mem:inventory;DB_CLOSE_DELAY=- 1;DB_CLOSE_ON_EXIT=FALSE  
spring.datasource.driverClassName=org.h2.Driver  
spring.datasource.username=sa  
spring.datasource.password=  
spring.h2.console.enabled=true  



spring.cloud.discovery.enabled=true  
server.port=8082  
spring.application.name=inventory-service  
#management.endpoints.web.exposure.include=*  

################################  
#Eureka  
################################  
eureka.client.service-url.defaultZone=http://localhost:8761/eureka

3.3 — Billing Service

// Обробляє інформацію про продукти.  
// Підтримує каталог продуктів.


// Використовує клієнтів Feign для комунікації з сервісами Customer та Inventory.


// Формує інформацію про рахунок, отримуючи дані з інших сервісів  

/*  
Він отримує продукти з оновленого InventoryService з вашими новими тенісними товарами.  
Створює рахунок для конкретного клієнта.  
Пов'язує кожен отриманий продукт з цим рахунком.  


// Використовує базу даних H2 в пам'яті  
package me.zegit.billingservice;  

import com.fasterxml.jackson.annotation.JsonProperty;  
import lombok.AllArgsConstructor;  
import lombok.Data;  
import lombok.NoArgsConstructor;  
import lombok.ToString;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.boot.CommandLineRunner;  
import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.cloud.openfeign.EnableFeignClients;  
import org.springframework.cloud.openfeign.FeignClient;  
import org.springframework.context.annotation.Bean;  
import org.springframework.data.jpa.repository.JpaRepository;  
import org.springframework.data.rest.core.annotation.RepositoryRestResource;  
import org.springframework.data.rest.core.config.Projection;  
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;  

import javax.persistence.*;  
import java.util.Collection;  
import java.util.Date;  
import java.util.List;  

@Entity  
@Data  
@NoArgsConstructor  
@AllArgsConstructor  
class Bill {  
 @Id  
 @GeneratedValue(strategy = GenerationType.IDENTITY)  
 private Long id;  
 private Date billingDate;  
 @Transient  
 private Customer customer;  
 @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)  
 private long customerID;  
 @OneToMany(mappedBy = "bill")  
 private Collection productItems;  
}  

@RepositoryRestResource  
interface BillRepository extends JpaRepository {  
}  

@Projection(name="full", types=Bill.class)  
interface BillProjection {  
 Long getId();  
 Date getBillingDate();  
 Long getCustomerID();  
 Collection getProductItems();  
}  

@RepositoryRestResource  
interface ProductItemRepository extends JpaRepository {  
 List findByBillId(Long billID);  
}  

@Entity  
@Data  
@NoArgsConstructor  
@AllArgsConstructor  
class ProductItem {  
 @Id  
 @GeneratedValue(strategy = GenerationType.IDENTITY)  
 private Long id;  
 @Transient  
 private Product product;  
 @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)  
 private long productID;  
 private double price;  
 private double quantity;  
 @ManyToOne  
 @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)  
 private Bill bill;  
}  

@Data  
class Customer {  
 private Long id;  
 private String name;  
 private String email;  
}  

@FeignClient(name = "CUSTOMER-SERVICE")  
interface CustomerService {  
 @GetMapping("/customers/{id}")  
 Customer findCustomerById(@PathVariable(name="id") Long id);  
}  

@Data  
class Product {  
 private Long id;  
 private String name;  
 private double price;  
}  

@FeignClient(name = "INVENTORY-SERVICE")  
interface InventoryService {  
 @GetMapping("/products/{id}")  
 Product findProductById(@PathVariable(name="id") Long id);  

 @GetMapping("/products")  
 PagedModel findAllProducts();  
}  

@SpringBootApplication  
@EnableFeignClients  
public class BillingServiceApplication {  

 public static void main(String[] args) {  
 SpringApplication.run(BillingServiceApplication.class, args);  
 }  

 @Bean  
 CommandLineRunner start(  
 BillRepository billRepository,  
 ProductItemRepository productItemRepository,  
 CustomerService customerService,  
 InventoryService inventoryService  
 ) {  
 return args -> {  
 // Отримуємо клієнта (припускаємо, що є клієнт з ID=1 в CustomerService)  
 Customer c1 = customerService.findCustomerById(1L);  
 System.out.println("----------------------------------------");  
 System.out.println("Отриманий клієнт: " + c1.getName() + " (" + c1.getEmail() + ")");  
 System.out.println("----------------------------------------");  

 // Створюємо новий рахунок для цього клієнта  


// Формує рахунок, отримуючи дані з інших сервісів  

/*  
Отримує продукти з (оновленого) InventoryService з вашими новими тенісними товарами.  
Створює рахунок для вказаного клієнта.  
Пов’язує кожен отриманий продукт з цим рахунком.  
*/  

package me.zegit.billingservice;  

import com.fasterxml.jackson.annotation.JsonProperty;  
import lombok.AllArgsConstructor;  
import lombok.Data;  
import lombok.NoArgsConstructor;  
import lombok.ToString;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.boot.CommandLineRunner;  
import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.cloud.openfeign.EnableFeignClients;  
import org.springframework.cloud.openfeign.FeignClient;  
import org.springframework.context.annotation.Bean;  
import org.springframework.data.jpa.repository.JpaRepository;  
import org.springframework.data.rest.core.annotation.RepositoryRestResource;  
import org.springframework.data.rest.core.config.Projection;  
import org.springframework.hateoas.PagedModel;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.PathVariable;  
import org.springframework.web.bind.annotation.RestController;  

import javax.persistence.*;  
import java.util.Collection;  
import java.util.Date;  
import java.util.List;  

@Entity  
@Data  
@NoArgsConstructor  
@AllArgsConstructor  
class Bill {  
 @Id  
 @GeneratedValue(strategy = GenerationType.IDENTITY)  
 private Long id;  
 private Date billingDate;  
 @Transient  
 private Customer customer;  
 @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)  
 private long customerID;  
 @OneToMany(mappedBy = "bill")  
 private Collection productItems;  
}  

@RepositoryRestResource  
interface BillRepository extends JpaRepository {  
}  

@Projection(name="full", types=Bill.class)  
interface BillProjection {  
 Long getId();  
 Date getBillingDate();  
 Long getCustomerID();  
 Collection getProductItems();  
}  

@RepositoryRestResource  
interface ProductItemRepository extends JpaRepository {  
 List findByBillId(Long billID);  
}  

@Entity  
@Data  
@NoArgsConstructor  
@AllArgsConstructor  
class ProductItem {  
 @Id  
 @GeneratedValue(strategy = GenerationType.IDENTITY)  
 private Long id;  
 @Transient  
 private Product product;  
 @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)  
 private long productID;  
 private double price;  
 private double quantity;  
 @ManyToOne  
 @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)  
 private Bill bill;  
}  

@Data  
class Customer {  
 private Long id;  
 private String name;  
 private String email;  
}  

@FeignClient(name = "CUSTOMER-SERVICE")  
interface CustomerService {  
 @GetMapping("/customers/{id}")  
 Customer findCustomerById(@PathVariable(name="id") Long id);  
}  

@Data  
class Product {  
 private Long id;  
 private String name;  
 private double price;  
}  

@FeignClient(name = "INVENTORY-SERVICE")  
interface InventoryService {  
 @GetMapping("/products/{id}")  
 Product findProductById(@PathVariable(name="id") Long id);  

 @GetMapping("/products")  
 PagedModel findAllProducts();  
}  

@SpringBootApplication  
@EnableFeignClients  
public class BillingServiceApplication {  

 public static void main(String[] args) {  
 SpringApplication.run(BillingServiceApplication.class, args);  
 }  

 @Bean  
 CommandLineRunner start(  
 BillRepository billRepository,  
 ProductItemRepository productItemRepository,  
 CustomerService customerService,  
 InventoryService inventoryService  
 ) {  
 return args -> {  
 // Отримуємо клієнта (припускаємо, що є клієнт з ID=1 в CustomerService)  
 Customer c1 = customerService.findCustomerById(1L);  
 System.out.println("----------------------------------------");  
 System.out.println("Отриманий клієнт: " + c1.getName() + " (" + c1.getEmail() + ")");  
 System.out.println("----------------------------------------");  

 // Створюємо новий рахунок для цього клієнта  

## application.properties  

################################  
#База даних  
################################  
spring.datasource.initialize=true  
spring.datasource.url=jdbc:h2:mem:billing;DB_CLOSE_DELAY=- 1;DB_CLOSE_ON_EXIT=FALSE  
spring.datasource.driverClassName=org.h2.Driver  
spring.datasource.username=sa  
spring.datasource.password=  
spring.h2.console.enabled=true  

################################  
#Інше  
################################  
#spring.cloud.discovery.enabled=false за замовчуванням spring буде шукати еureka, щоб бути зареєстрованим, тому false, якщо ви не хочете  
spring.cloud.discovery.enabled=true  
server.port=8084  
spring.application.name=billig-service  
#management.endpoints.web.exposure.include=*  

################################  
#Eureka  
################################  
eureka.client.service-url.defaultZone=http://localhost:8761/eureka  

3.4 — API Gateway

@SpringBootApplication  
public class GatewayServiceApplication {  

 public static void main(String[] args) {  
 SpringApplication.run(GatewayServiceApplication.class, args);  
 }  

 @Bean  
 RouteLocator gatewayRoutes(RouteLocatorBuilder builder) {  
 return builder.routes()  
 // Існуючий маршрут до публічного API для міст  
 .route(r -> r.path("/publicCities/**")  
 .filters(f -> f  
 .addRequestHeader("X-RapidAPI-Key", "YOUR_RAPIDAPI_KEY")  
 .addRequestHeader("X-RapidAPI-Host", "wft-geo-db.p.rapidapi.com")  
 .rewritePath("/publicCities/(?.*)", "/${segment}")  
 )  
 .uri("https://wft-geo-db.p.rapidapi.com/v1/geo/cities"))  

 // публічні маршрути API  
 .route(r -> r.path("/covidStats/**")  
 .filters(f -> f  
 .addRequestHeader("X-RapidAPI-Key", "YOUR_RAPIDAPI_KEY")  
 .addRequestHeader("X-RapidAPI-Host", "covid-193.p.rapidapi.com")  
 .rewritePath("/covidStats/(?.*)", "/${segment}")  
 )  
 .uri("https://covid-193.p.rapidapi.com"))  

 .route(r -> r.path("/weather/**")  
 .filters(f -> f  
 .addRequestHeader("X-RapidAPI-Key", "YOUR_RAPIDAPI_KEY")  
 .addRequestHeader("X-RapidAPI-Host", "weatherapi-com.p.rapidapi.com")  
 .rewritePath("/weather/(?.*)", "/${segment}")  
 )  
 .uri("https://weatherapi-com.p.rapidapi.com"))  

 .build();  
 }  

 @Bean  
 DiscoveryClientRouteDefinitionLocator dynamicRoutes(ReactiveDiscoveryClient rdc, DiscoveryLocatorProperties dlp) {  
 return new DiscoveryClientRouteDefinitionLocator(rdc, dlp);  
 }  
}  

//статичне маршрутизування
//lb: балансувальник навантаження

  • Що таке балансування навантаження за методом Round-Robin? Балансування навантаження за методом Round-Robin — це один з найпростіших методів розподілу запитів клієнтів між групою серверів.
  • Перебираючи список серверів у групі, балансувальник навантаження за методом Round-Robin
    • перенаправляє запит клієнта до кожного сервера по черзі.
    • //.route(r -> r.path("/products/**").uri("lb://INVENTORY-SERVICE"))
    • */

### application.properties  

spring.application.name=gateway-service  
server.port=8888  
spring.cloud.discovery.enabled=true  
management.endpoints.web.exposure.include=*

## 3.5 — Виявлення сервісів за допомогою Eureka

/* Сервер Eureka дозволяє кожному мікросервісу реєструвати себе при запуску.
Інші сервіси можуть потім знайти та взаємодіяти з ним без необхідності вказувати URL-адреси в коді.

За замовчуванням працює на порту 8761.
Надає панель управління для перегляду зареєстрованих сервісів та їхнього статусу здоров’я.
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServiceApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServiceApplication.class, args);
}
}
```

### application.properties  

spring.application.name=eureka-service  
server.port=8761  
eureka.client.fetch-registry=false  
eureka.client.register-with-eureka=true

4 — Розгортання: Поради

Порядок запуску

– Сервер Eureka має запускатися першим.

– Мікросервіси реєструються з Eureka після його запуску.

– API Gateway може запускатися після того, як Eureka буде доступний.

Налаштування

– Кожен сервіс має свій власний файл application.yml/properties.

– Для більш складних налаштувань ви можете централізувати конфігурації за допомогою Spring Cloud Config.

Масштабування

– Ви можете запустити кілька екземплярів сервісів з високим попитом (наприклад, Inventory Service) за допомогою Gateway.

5 — Висновок

Код, представлений тут, є відправною точкою для тих, хто хоче почати роботу з мікросервісами за допомогою Spring Boot і Spring Cloud. Дотримуючись найкращих практик — таких як впровадження API Gateway, використання Eureka Server для виявлення сервісів та використання Feign клієнтів для комунікації — ви зможете створити надійну, модульну систему, яку буде легше підтримувати та розвивати з часом.

Перекладено з: Microservices Architecture : Designing Scalable Java Applications with Spring

Leave a Reply

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