← Назад к вопросам

Как микросервис узнает как обратиться к другому при прямом вызове

2.8 Senior🔥 171 комментариев
#REST API и микросервисы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как микросервис узнаёт адрес другого микросервиса при прямом вызове

Это классическая задача в микросервисной архитектуре. Существует несколько подходов к обнаружению сервисов (Service Discovery).

1. Service Registry (Реестр сервисов)

Принцип: Каждый микросервис регистрирует себя в центральном реестре, другие сервисы запрашивают адреса у реестра.

Популярные решения: Eureka, Consul, Nacos, Zookeeper

// Spring Cloud Eureka Client
// application.yml
eureka:
  instance:
    hostname: payment-service
    instance-id: ${spring.application.name}:${server.port}
  client:
    service-url:
      defaultZone: http://eureka-server:8761/eureka

spring:
  application:
    name: payment-service
  cloud:
    client:
      ip-address: 192.168.1.100

// Микросервис регистрируется автоматически
@SpringBootApplication
@EnableEurekaClient
public class PaymentServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(PaymentServiceApplication.class, args);
    }
}

Как другой сервис находит адрес:

// Order Service запрашивает адрес Payment Service
@Service
public class OrderService {
    private final RestTemplate restTemplate;
    private final DiscoveryClient discoveryClient;

    public void placeOrder(Order order) {
        // Способ 1: DiscoveryClient для поиска
        List<ServiceInstance> instances = discoveryClient
            .getInstances("payment-service");
        
        if (!instances.isEmpty()) {
            ServiceInstance instance = instances.get(0);
            String url = instance.getUri() + "/api/v1/payments";
            restTemplate.postForObject(url, payment, Payment.class);
        }
    }
}

Способ 2: Использование LoadBalancer (более удобно)

@Service
public class OrderService {
    private final RestTemplate restTemplate; // с @LoadBalanced

    public void placeOrder(Order order) {
        // Вместо реального IP используем имя сервиса
        // LoadBalancer сам найдёт адрес в реестре
        restTemplate.postForObject(
            "http://payment-service/api/v1/payments",
            payment,
            Payment.class
        );
    }
}

@Configuration
public class RestTemplateConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Способ 3: OpenFeign (самый удобный)

@FeignClient(name = "payment-service")
public interface PaymentServiceClient {
    @PostMapping("/api/v1/payments")
    Payment createPayment(@RequestBody Payment payment);
}

@Service
public class OrderService {
    @Autowired
    private PaymentServiceClient paymentClient;

    public void placeOrder(Order order) {
        // Просто вызываем метод, адрес решается автоматически
        Payment payment = paymentClient.createPayment(order.getPayment());
    }
}

2. DNS (Domain Name System)

Принцип: Каждому сервису дается DNS имя, которое в runtime разрешается в IP адрес.

// Kubernetes пример
// Сервис доступен как: payment-service.default.svc.cluster.local

@Service
public class OrderService {
    private final RestTemplate restTemplate;

    public void placeOrder(Order order) {
        // DNS разрешает имя в IP внутри кластера
        restTemplate.postForObject(
            "http://payment-service:8080/api/v1/payments",
            payment,
            Payment.class
        );
    }
}

Kubernetes DNS:

apiVersion: v1
kind: Service
metadata:
  name: payment-service
spec:
  selector:
    app: payment
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080

Когда микросервис запускается в Kubernetes, он может обратиться к другому как http://payment-service:8080, и Kubernetes DNS сам найдёт Pod.

3. Config Server (Статическая конфигурация)

Принцип: Адреса жестко кодированы в конфигурационных файлах или Config Server.

# application.yml в Order Service
services:
  payment:
    url: http://payment-service:8080
  inventory:
    url: http://inventory-service:8080

# Или через Spring Cloud Config Server
spring:
  cloud:
    config:
      uri: http://config-server:8888
@Configuration
@PropertySource("application.yml")
public class ServiceUrlConfig {
    @Value("${services.payment.url}")
    private String paymentServiceUrl;

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public String getPaymentServiceUrl() {
        return paymentServiceUrl; // http://payment-service:8080
    }
}

4. Service Mesh (Продвинутый подход)

Решения: Istio, Linkerd, Consul Connect

# Istio VirtualService
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: payment-service
spec:
  hosts:
  - payment-service
  http:
  - route:
    - destination:
        host: payment-service
        port:
          number: 8080

Микросервис просто вызывает другой по имени, а Service Mesh обрабатывает маршрутизацию, балансировку, retry и т.д.

5. Прямой вызов без Discovery (Плохая практика)

// ❌ Плохо — жёсткий IP
private static final String PAYMENT_SERVICE_URL = "http://192.168.1.100:8080";

// ❌ Плохо — имя хоста, но не масштабируется
private static final String PAYMENT_SERVICE_URL = "http://payment-server.local:8080";

Это неудобно, когда сервис переезжает на другой IP/хост.

Полный пример: Spring Cloud микросервисы

Eureka Server:

server:
  port: 8761
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

Payment Service (регистрируется):

@SpringBootApplication
@EnableEurekaClient
@RestController
@RequestMapping("/api/v1/payments")
public class PaymentService {
    @PostMapping
    public Payment createPayment(@RequestBody Payment payment) {
        payment.setId(UUID.randomUUID());
        return payment;
    }
}

Order Service (вызывает Payment Service):

@SpringBootApplication
@EnableEurekaClient
public class OrderService {
    public static void main(String[] args) {
        SpringApplication.run(OrderService.class, args);
    }
}

@FeignClient(name = "payment-service")
public interface PaymentClient {
    @PostMapping("/api/v1/payments")
    Payment createPayment(@RequestBody Payment payment);
}

@Service
public class OrderServiceImpl {
    @Autowired
    private PaymentClient paymentClient;

    public Order placeOrder(OrderRequest request) {
        // Feign автоматически найдёт payment-service в Eureka
        Payment payment = paymentClient.createPayment(request.getPayment());
        return new Order(payment);
    }
}

Сравнение подходов

ПодходПреимуществаНедостатки
Service Registry (Eureka)Динамическое, масштабируетсяДополнительный компонент
DNS (Kubernetes)Просто, встроенноеТолько в Kubernetes
Config ServerСтатическое, надёжноеНе масштабируется, нужно перезагружать
Service MeshВсе фичи, прозрачнаСложнее, требует инфраструктуры

Вывод

Для современных микросервисов используй:

  1. Kubernetes + DNS — если используешь K8s
  2. Spring Cloud + Eureka + OpenFeign — для классического стека Spring
  3. Istio Service Mesh — для максимального контроля и надёжности

Не жёсткой кодируй IP адреса и не используй прямые hostname'ы — всегда используй Service Discovery!

Как микросервис узнает как обратиться к другому при прямом вызове | PrepBro