RATSENO

[Spring]Spring Cloud Netflix - Eureka + Ribbon 본문

DEV/SPRING

[Spring]Spring Cloud Netflix - Eureka + Ribbon

RATSENO 2020. 1. 29. 18:03

이전 포스팅
[Spring]Spring Cloud Netflix - Eureka[1]

[Spring]Spring Cloud Netflix - Eureka[2]

[Spring]Spring Cloud Netflix - Eureka[3]

 에 이어서 클라이언트 로드 밸런서인 Ribbon에 대해서 알아보겠습니다.


로드 밸런싱(load balancing)이란?

간단하게 설명하면 한대의 서버에 몰리는 부하를 분산시키는 것입니다.

 

기존의 서버 사이드 로드 밸런싱L4스위치(H/W)를 이용합니다.

4계층의 Port를 이용하여 트래픽을 여러 서버에게 분산해줍니다. 하지만 위에 구조는 몇가지 문제점이 있습니다.

  • 서버사이드 로드밸런서를 위해 L4 스위치와 같은 장비 H/W가 필요합니다

  • 위의 이유 때문에 비용과 유연성의 부담을 안게됩니다.

  • 트래픽을 분산해줄 Server 1~4의 주소를 알고 있어야합니다.

  • 로드밸런싱 방법이 제한적입니다.

이러한 서버 사이드 로드밸런싱의 문제점을 해결하기위해 나온 개념이 클라이언트 사이드 로드 밸런싱 입니다. 

클라이언트 사이드 로드 밸런싱은 L4 스위치가 중간에서 하던 로드 밸런서의 역할을 클라이언트 소프트웨어로 대체하는 것입니다.

이 클라이언트 사이드 로드 밸런서 역할을 하는것이 이번 포스팅에서 알아볼 Ribbon 입니다.

Ribbon은 분산 처리 방법으로 여러 서버를 라운드 로빈 방식으로 부하 분산 기능을 제공합니다. (여러 알고리즘 사용 가능)

 

이전 포스팅에서 employee-producer, employee-consumer 두가지 모듈을 사용하였습니다.

employee-consumer 모듈이 employee-producer 모듈을 사용하지만 이것이 producer 모듈에 부하를 주고있다고 가정하였습니다.

이를 해결하기 위해서 employee-producer 모듈 인스턴스를 추가로 하나 더 배포하겠습니다.

이제 하나의 요청을 두개의 서비스중 하나로 라우팅 하기위해 로드 밸런싱을 하겠습니다.

이전 포스팅에서 만들어둔 eureka-server, employee-producer, employee-consumer 모듈을 수정해보겠습니다.

먼저 employee-producer 모듈입니다.

application.properties 파일에 해당 인스턴스의 id를 랜덤으로 생성하도록 설정하겠습니다.

eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka
eureka.instance.instanceId=${spring.application.name}:${spring.application.instance_id:${random.value}}

같은 내용의 모듈을 두개의 인스턴스로 띄우기 위해서 intellij를 사용할 경우 간편한 설정을 통하여

포트번호를 다르게 셋팅하여 띄울수 있습니다.

 

참고링크 : 어플리케이션 여러개 띄우기 

 

RATSENO/TIL

하루하루 공부한것 정리 및 이슈 정리. Contribute to RATSENO/TIL development by creating an account on GitHub.

github.com

80, 81포트로 두개의 employee-producer 모듈을 띄울수 있습니다.

eureka-server를 Run 한 후 http://localhost:8761/ 에 접속하여 eureka page를 확인해 보면,

employee-producer 인스턴스가 두개 등록된것을 확인할 수 있습니다.

다음은 employee-consumer 모듈을 수정해보겠습니다.

pom.xml에 spring cloud ribbon starter dependency를 추가합니다.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>employee-consumer</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.1.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Camden.SR6</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

이어서 ConsumerControllerClient를 수정하겠습니다. 이전에 @Autowired로 주입한 DiscoveryClient 대신 LoadBalancerClient로 변경합니다.

또한 기존에 서버 기동 시에 호출하는 방식을 편의를 위해 서비스를 노출해서 직접 호출하는 방식으로 변경하도록 하겠습니다.

package com.example.controller;

import java.io.IOException;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
//@Controller
@RestController
public class ConsumerControllerClient {

    @Autowired
    //private DiscoveryClient discoveryClient;
    private LoadBalancerClient loadBalancerClient;

    @GetMapping("/eureka/client")
    public void getEmployee() throws RestClientException, IOException {
        /*
        List<ServiceInstance> instances=discoveryClient.getInstances("employee-producer");
        ServiceInstance serviceInstance=instances.get(0);
        String baseUrl=serviceInstance.getUri().toString();
        */
        ServiceInstance serviceInstance = loadBalancerClient.choose("employee-producer");
        System.out.println("#####service-instance-uri:"+serviceInstance.getUri());
        String baseUrl = serviceInstance.getUri().toString();

        baseUrl=baseUrl+"/employee";

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> response=null;
        try{
            response=restTemplate.exchange(baseUrl,
                    HttpMethod.GET, getHeaders(),String.class);
        }catch (Exception ex)
        {
            System.out.println(ex);
        }
        System.out.println(response.getBody());
    }

    private static HttpEntity<?> getHeaders() throws IOException {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
        return new HttpEntity<>(headers);
    }
}

Ribbon(로드 밸런서)은 이제 내부 알고리즘을 사용하여 두개의 employee-producer 인스턴스 중 하나의 인스턴스를 호출하여 로드 밸런싱을 합니다.

 

이를 확인하기 위해서 employee-consumer 모듈도 Run하여 노출한 서비스 url로 여러번 호출해보겠습니다.

 

위와 같이 다른 포트의 두개의 인스턴스를 번갈아 가면 호출하는것을 확인할 수 있습니다.

 

이상으로 아주 간단하게 eureka와 ribbon을 이용한 클라이언트 로드밸런싱에 대해서 알아보았습니다.

부족한 점은 댓글로 언제든지 지적해주시면 감사히 수정하도록 하겠습니다.

 

Github : https://github.com/RATSENO/spring-cloud-example/blob/master/README.md

Comments