Client-side load balancing

2024. 12. 11. 14:06Spring Microservice

Spring Boot의 Client-Side Load Balancing

Client-Side Load Balancing은 서비스 소비자가 서비스 요청을 보내기 전에 직접 서비스 인스턴스 목록을 가져와 적절한 인스턴스를 선택하여 요청을 분배하는 방식입니다. 이 방식은 클라이언트가 로드 밸런싱 책임을 지며, Spring Boot에서는 주로 Spring Cloud LoadBalancer를 통해 구현됩니다.

 


클라이언트의 정의

위에서 설명한 클라이언트(Client)는 일반적으로 생각하는 웹 브라우저(웹 클라이언트)가 아닌, 애플리케이션 간 통신에서 다른 서비스를 호출하는 마이크로서비스나 애플리케이션을 의미합니다.

이 경우 클라이언트는 마이크로서비스 환경에서 다른 서비스를 호출하고 요청을 처리하는 서비스 소비자(Service Consumer)입니다. 예를 들어:

  • 서비스 제공자 (Service Provider): 데이터를 제공하거나 특정 기능을 실행하는 서비스.
  • 서비스 소비자 (Service Consumer, 클라이언트): 다른 서비스로부터 데이터를 요청하거나 기능을 호출하는 서비스.

즉, 서비스 디스커버리와 로드 밸런싱이 필요한 클라이언트는 백엔드 서비스비즈니스 로직을 실행하는 애플리케이션입니다.

예제 시나리오

  1. 웹 브라우저:
    • 사용자는 웹 브라우저에서 특정 URL로 요청을 보냅니다.
    • 이 요청은 클라이언트가 아닌, 백엔드 애플리케이션(예: API Gateway 또는 Spring Boot 애플리케이션)으로 전달됩니다.
  2. 클라이언트 (Service Consumer):
    • 백엔드 애플리케이션(예: Spring Boot 애플리케이션)은 로드 밸런싱을 적용하여 다른 마이크로서비스로 요청을 보냅니다.
    • 이 백엔드 애플리케이션이 바로 클라이언트입니다.

간단한 구조

[사용자] → [웹 브라우저] → [API Gateway 또는 Backend Service (클라이언트)] → [다른 서비스 (서비스 제공자)]
  • 웹 브라우저: 사용자가 직접 상호작용하는 인터페이스.
  • 클라이언트: 백엔드의 일부로 동작하며, 사용자의 요청을 처리하기 위해 다른 서비스를 호출.

클라이언트 예시

클라이언트 코드 (Service Consumer):

@RestController
public class ClientController {

    private final RestTemplate restTemplate;

    public ClientController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @GetMapping("/consume")
    public String consumeService() {
        // 다른 서비스 호출
        String url = "http://SERVICE-NAME/api/resource";
        return restTemplate.getForObject(url, String.class);
    }
}
  • 위의 코드에서 ClientController는 클라이언트로 동작합니다.
  • 서비스 이름(SERVICE-NAME)을 사용하여 다른 서비스(서비스 제공자)로 요청을 보냅니다.

summary

  • 웹 브라우저: 사용자 인터페이스로 사용되며, 로드 밸런싱과 관련이 없습니다.
  • 클라이언트: 백엔드 애플리케이션 또는 마이크로서비스에서 다른 서비스를 호출하는 소비자입니다.
  • 스프링 부트의 Client-Side Load Balancing은 마이크로서비스 간의 통신에서 작동하며, 웹 브라우저와는 직접적인 관련이 없습니다.

Client-Side Load Balancing의 작동 원리

  1. 서비스 디스커버리:
    • 클라이언트는 Eureka, Consul, 또는 Kubernetes와 같은 서비스 레지스트리에서 서비스 인스턴스의 정보를 가져옵니다.
    • 서비스 이름으로 요청하면, 해당 서비스의 모든 가용 인스턴스 목록을 반환받습니다.
  2. 로드 밸런싱:
    • 클라이언트는 가용 인스턴스 목록에서 적절한 인스턴스를 선택합니다.
    • 선택 기준은 Round Robin, Random, Weighted 등 다양한 전략이 사용됩니다.
  3. 요청 전달:
    • 선택된 인스턴스에 요청을 보냅니다.

 

Spring Boot에서의 Client-Side Load Balancing 구현

Spring Boot는 이전에 Netflix Ribbon을 사용했지만, Spring Cloud 2020 릴리스 이후에는 Spring Cloud LoadBalancer가 디폴트 client side 로드 밸런싱 라이브러리로 사용됩니다.

Spring Cloud LoadBalancer의 주요 기능

  • 다양한 로드 밸런싱 알고리즘 제공 (Round Robin, Random 등).
  • Eureka, Consul, Kubernetes와의 통합.
  • 커스터마이징 가능한 로드 밸런싱 전략.

Spring Boot 설정 및 구현 예시

1. 의존성 추가

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2. Eureka 설정

application.yml:

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true

3. RestTemplate 사용

Spring Boot에서 RestTemplate를 클라이언트 사이드 로드 밸런싱과 함께 사용할 수 있습니다.

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

@LoadBalanced를 사용하면 RestTemplate이 서비스 이름(SERVICE-NAME)을 IP 주소로 변환하고, 적절한 인스턴스를 선택합니다.

4. 서비스 호출

@RestController
public class ClientController {

    private final RestTemplate restTemplate;

    public ClientController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @GetMapping("/consume")
    public String consumeService() {
        // "SERVICE-NAME"은 Eureka에 등록된 서비스 이름입니다.
        String url = "http://SERVICE-NAME/api/resource";
        return restTemplate.getForObject(url, String.class);
    }
}

 

로드 밸런싱 전략 커스터마이징

Spring Cloud LoadBalancer는 기본적으로 Round Robin 전략을 사용합니다. 하지만, 전략을 변경하거나 커스터마이징할 수도 있습니다.

1. 기본 전략 변경

Round Robin 대신 Random 전략으로 변경하려면 다음과 같이 설정합니다.

@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, 
                                                         LoadBalancerClientFactory loadBalancerClientFactory) {
    String serviceId = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
    return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(serviceId, ServiceInstanceListSupplier.class), serviceId);
}

2. 커스터마이징 전략

특정 메타데이터를 기반으로 로드 밸런싱 전략을 구현할 수도 있습니다.

예: 서비스의 가중치 기반 선택

public class WeightedLoadBalancer extends RoundRobinLoadBalancer {

    public WeightedLoadBalancer(ServiceInstanceListSupplier supplier, String serviceId) {
        super(supplier, serviceId);
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        return super.choose(request).flatMap(instance -> {
            // 가중치 기반 선택 로직 추가
            return Mono.just(instance);
        });
    }
}

 

Spring Cloud Gateway와의 통합

Spring Cloud LoadBalancer는 RestTemplate뿐만 아니라 Spring Cloud Gateway와도 통합 가능합니다. 이는 서버 사이드 디스커버리와 함께 사용할 때 유용합니다.

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true

이 설정은 Gateway가 Eureka 서비스 레지스트리에서 서비스 목록을 조회하고, 클라이언트 요청을 적절한 서비스로 전달하도록 합니다.

 

Client-Side Load Balancing의 장점과 단점

장점

  1. 네트워크 최적화:
    • 클라이언트가 서비스 인스턴스를 직접 선택하기 때문에 네트워크 지연 시간이 줄어듭니다.
  2. 유연성:
    • 클라이언트가 로드 밸런싱 전략을 자유롭게 변경하거나 커스터마이징할 수 있습니다.
  3. 분산 처리:
    • 로드 밸런싱 로직이 분산되어 서버 로드가 줄어듭니다.

단점

  1. 클라이언트 복잡성 증가:
    • 클라이언트가 서비스 디스커버리와 로드 밸런싱 책임을 가지므로 코드가 복잡해질 수 있습니다.
  2. 캐싱 문제:
    • 클라이언트는 서비스 인스턴스 목록을 캐싱해야 하므로, 레지스트리 정보가 변경될 경우 실시간 반영이 어려울 수 있습니다.
  3. 스케일링:
    • 많은 클라이언트가 서비스 레지스트리에 직접 접근하면, 레지스트리의 부하가 증가할 수 있습니다.

 

Client-Side Load Balancing이 필요한 경우

  • 마이크로서비스 환경: 서비스 인스턴스가 동적으로 추가/제거되는 환경에서 유용합니다.
  • 고가용성 요구: 클라이언트가 다수의 서비스 인스턴스 중 하나를 선택해 요청을 분배하여 고가용성을 제공합니다.
  • 클라이언트 제어: 클라이언트에서 직접 로드 밸런싱 전략을 설정하거나 수정해야 할 경우 적합합니다.


Spring Boot의 Client-Side Load Balancing은 마이크로서비스 환경에서 유연성과 확장성을 제공합니다. Spring Cloud LoadBalancer를 사용하여 Eureka와 같은 서비스 레지스트리와 통합하면, 복잡한 분산 시스템에서 서비스 간의 연결을 간단히 관리할 수 있습니다. 또한, 로드 밸런싱 전략을 커스터마이징함으로써 다양한 비즈니스 요구사항을 충족할 수 있습니다.