본문 바로가기
IT/Spring Cloud

Spring Cloud 시리즈 5 - Loadbalancer (feat. Ribbon)

by 최고영회 2023. 6. 1.
728x90
반응형
SMALL

Load Balancing 란?

  • 로드밸런싱이란 네트워크 기술의 일종으로 둘 혹은 셋 이상의 컴퓨터 자원들에게 작업을 나누는 것을 의미 한다. 
  • 보통 고가용성 서비스를 제공하기 위해 인스턴스를 여러개 띄우고 트래픽을 분산해 각 인스턴스에게 요청을 전달한다.

 

Load balancing 의 종류

  • L2: Mac 주소를 바탕으로 부하 분산
  • L3: ICMP를 이용하여 서버의 IP 주소가 통신 가능한 상태인지 확인하여 부하 분산 
  • L4: Transport 계층, IP 주소와 포트 번호 부하 분산 (TCP, UDP 포트 정보를 바탕으로 분석)
  • L7: Application 계층, URL 또는 HTTP 헤더에서 부하 분산 (HTTP, FTP..)

 

Load balancing algorithm

  • 라운드 로빈: 대상 서버에 순서대로 할당하는 방식 
                         대상 서버의 성능이 동일하고 처리 시간이 짧을 경우 균등하게 분산이 이루어지기 때문에 이 방식을 사용
  • 가중 라운드 로빈 방식: 각 서버에 서로 다른 처리 용량을 지정하고 각 서버의 가중치를 부여해서 지정한 값만큼
                                         정하여 전달하는 방식
  • 최소 연결 방식: 연결 수가 가장 적은 서버에 네트워크 연결 방향을 정하고 동적 분산 알고리즘으로 서버에 대한 현재
                              연결 수를 동적으로 카운트하여 동적으로 변하는 요청에 대한 부하를 분산시키는 방식 
  • IP 해싱 방식: 사용자의 IP를 해싱하여 분배하는 방식, 사용자는 항상 같은 서버로 연결되는 것을 보장

 

Client side load balancing

 - 이전 포스팅에서 Eureka 를 통해 여러 인스턴스의 정보를 받아와 API 를 호출할 수 있었다.

@RestController @RequiredArgsConstructor @Slf4j
public class TestController {
    final RestTemplate restTemplate;
    final DiscoveryClient discoveryClient;

    @GetMapping("/b/hello")
    public ResponseEntity<String> hello() {
        String baseUrl = "";
        List<ServiceInstance> list = discoveryClient.getInstances("YHKIM-EUREKA-CLIENT-A");
        for (ServiceInstance instance : list) {
            log.info("{}", instance.toString());
        }  
        baseUrl = list.get(0).getUri().toString();
        return ResponseEntity.ok(restTemplate.getForObject(baseUrl + "/a/hello", String.class));
    }
}

 - 사실 여기에서 list.get(0); 이 아니라 Random 하게 가져오면 random 한 client-side load balancing 이 되는것이다.

   아니면 사용자 IP 에 따라 분산시킬수도 있고 기타 여러 다양한 방식을 client side 에서 구현할 수 있다.

 - Ribbon 은 현재 netflix-oss-hystrxi 처럼 Spring Cloud 에서 대체하는 기술을 내놓은 상태이기 때문에
   바로 Spring Cloud Loadbalancer 를 이용해 본다.

 - Spring Cloud Loadbalancer 는 round-robin 과 random 방식을 지원한다. 

@RestController @RequiredArgsConstructor @Slf4j
public class TestController {
    final RestTemplate restTemplate;
    final DiscoveryClient discoveryClient;
    final LoadBalancerClient loadBalancerClient;

    @GetMapping("/b/hello")
    public ResponseEntity<String> hello() {
        String baseUrl = "";
        List<ServiceInstance> list = discoveryClient.getInstances("YHKIM-EUREKA-CLIENT-A");
        for (ServiceInstance instance : list) {
            log.info("{}, {}", instance.toString(), instance.getMetadata().toString());
        }
        if (!CollectionUtils.isEmpty(list)) {
            baseUrl = list.get(0).getUri().toString();
        }
        return ResponseEntity.ok(restTemplate.getForObject(baseUrl + "/a/hello", String.class));
    }

    @GetMapping("/c/hello")
    public ResponseEntity<String> helloWithLb() {
        ServiceInstance instance = loadBalancerClient.choose("YHKIM-EUREKA-CLIENT-A");
        String baseUrl = instance.getUri().toString();
        log.info("load-balancing: {}", baseUrl+"/a/hello");
        return ResponseEntity.ok(restTemplate.getForObject(baseUrl + "/a/hello", String.class) + ", port:"+instance.getPort());
    }
}

방법은 너무 간단하다. 

LoadBalancerClient 를 이용하는 것이고 eureka 에서 DiscoveryClient 를 이용했던 것 처럼

Eureka 에 등록되어 있는 서비스 이름으로 loadbalancerClient 에서 find 해서 사용하면 된다.

LoadBalancerClient 는 기본적으로 라운드 로빈 방식을 이용한다.

 

yhkim-eureka-client-a app 의 인스턴스를 3개 띄우고 

port: 64839
Port: 64815
Port: 64266

Eureka 에 잘 등록된 것을 확인해 본다. 

 

그리고 /c/hello 를 호출해서 client side 에서 LoadBalancing 되는지 확인해 보자. 

 

새로고침 할 때 마다 port:64815, 64839, 64266... 으로 반복되는 것을 볼 수 있다. 

이중 port 64815 인스턴스를 stop 시키면

 

Eureka 에서도 정상적으로 빠진 것을 확인할 수 있고 

 

테스트용 client 에서도 30초 주기로 eureka 서버로부터 등록된 정보를 받아 갱신하기 때문에

port 64266 다음에 64815 를 호출하지 않고 바로 64839 를 호출하는 것을 볼 수 있다.

 

Spring Cloud Loadbalancer 는 기본적으로 라운드로빈 알고리즘을 이용한다.

Quiz.

 - 2개의 instance 중 하나에 문제가 발생할 경우 다른 instance 에 요청하게 하려면 어떻게 해야 할까?

 

 

너무 쉽다.... 이전 포스팅에서 다루었던 Recilience4j 의 Retry 를 이용하면 된다. 

@Retry(name = "callExternalApi", fallbackMethod = "fallbackLoadbalance")

port 65071 을 먼저 시도해보고 500 internal server error 를 받아서 다시 65063 port 에 시도하는 것을 볼 수 있다.

이로써 보다 더 안정적인 로드밸런싱이 되었다. 

 

Spring Cloud Loadbalancer 에서 알고리즘을 변경하는 방법은 다음에 추가로 정리하기로 하자..

728x90
반응형
LIST