앞선 "서비스 디스커버리"포스팅에서 서비스 디스커버리와 FeignClient에 대해서 FeignClient가 무엇인지, FeignClient의 주요 특징에 대해서 알아보았습니다.
이번 포스팅에서는 FeignClient가 속하는 클라이언트 사이드 로드 밸런싱의 일환으로, FeignClient와 Ribbon에 대해서 알아보겠습니다.
로드 밸런싱란❓
로드 밸런싱(Load Balancing)은 네트워크 트래픽을 여러 서버 또는 서비스 인스턴스에 고르게 분산하여, 서버의 부하를 줄이고, 시스템의 성능을 최적화하고 가용성을 높이는 기술입니다.
로드 밸런싱의 주요 목적
- 성능 최적화
- 로드 밸런싱은 트래픽을 여러 서버에 분산시킴으로써, 각 서버에 걸리는 부하를 줄이고 시스템의 전체 성능을 향상시킵니다.
- 이를 통해 서버가
과부하되지 않도록 방지할 수 있습니다.
- 고가용성 보장
- 로드 밸런서는 하나의 서버가 장애가 나거나 비정상 상태에 빠졌을 때, 트래픽을 다른 정상적인 서버로 자동으로 전환하여 시스템의 가용성을 유지합니다.
- 유연한 확장성
- 트래픽의 증가에 따라 서버를 추가하거나 제거할 수 있으며, 로드 밸런서는 이러한 변화를 자동으로 반영하여 적절히 요청을 분산시킵니다.
- 신뢰성 향상
- 로드 밸런서는 서버의 상태를 모니터링하여 장애가 발생한 서버를 감지하고, 요청을 다른 정상적인 서버로 우회시킵니다. 이로 인해 시스템의 신뢰성을 높일 수 있습니다.
로드 밸런싱의 종류
서버 사이드 로드 밸런싱
- 물리적 또는 가상 장비로서 네트워크 트래픽을 수신하고, 서버 풀의 여러 서버에 요청을 분산합니다.
- ex) 하드웨어 로드 밸런서, 소프트웨어 기반 로드 밸런서
클라이언트 사이드 로드 밸런싱
- 클라이언트 애플리케이션이 서비스 디스커버리 및 로드 밸런싱을 담당합니다.
- ex) FeignClient, Ribbon
DNS 기반 로드 밸런싱
- DNS 서버가 요청을 받아, 여러 IP 주소 중 하나를 응답으로 제공하여 트래픽을 분산시킵니다. 이는 DNS 라운드로빈 방식으로 구현됩니다.
애플리케이션 기반 로드 밸런싱
- 애플리케이션 계층에서 로드 밸런싱을 수행하며, HTTP/HTTPS 트래픽의 라우팅을 관리합니다.
- ex) AWS의 Application Load Balancer
클라이언트 사이드 로드 밸런싱
클라이언트 사이드 로드 밸런싱(Client-Side Load Balancing)은 클라이언트 애플리케이션이 서버나 서비스 인스턴스의 목록을 유지하고, 요청을 적절히 분산시키는 방식의 로드 밸런싱입니다.
클라이언트는 서버의 목록을 가지고 있으며, 이를 바탕으로 로드 밸런싱을 수행합니다.
즉, 클라이언트가 서비스 인스턴스의 선택과 요청의 분산을 직접 관리하는 방법입니다.
FeignClient란❓
FeignClient는 Spring Cloud에서 제공하는 선언적 웹 서비스 클라이언트입니다.
FeignClient를 사용하면 마이크로서비스 아키텍처(MSA)에서 서비스 간의 통신을 더 간편하고 직관적으로 처리할 수 있습니다.
FeignClient는 HTTP 요청을 인터페이스 기반으로 정의하고, Spring Cloud와 통합되어 자동으로 로드 밸런싱과 서비스 디스커버리 기능을 제공합니다.
FeignClient의 주요 특징 ✅
- 선언적 인터페이스
- FeignClient를 사용하면 HTTP 요청을 Java 인터페이스의 메서드로 선언할 수 있습니다.
- 예를 들어, REST API 호출을 위해 별도의 HTTP 클라이언트 코드를 작성할 필요 없이 인터페이스에 메서드와 URL을 정의합니다.
- 자동 서비스 디스커버리
- FeignClient는 서비스 이름을 기반으로 Eureka와 같은 서비스 디스커버리 서버에서 해당 서비스의 위치를 조회합니다.
- 서비스의 위치(IP와 포트)는 동적으로 변할 수 있기 때문에, FeignClient는 이를 자동으로 처리합니다.
- 내장 로드 밸런싱
- FeignClient는 Ribbon과 통합❗️되어 있어, 여러 인스턴스가 존재하는 경우 로드 밸런싱을 통해 적절한 인스턴스에 요청을 분산합니다.
- 이는 클라이언트 측에서 로드 밸런싱을 자동으로 처리함으로써, 서비스의 가용성과 성능을 높입니다.
- 간단한 사용법
- FeignClient를 정의하고 @FeignClient 어노테이션을 사용하여 인터페이스를 등록합니다.
- @GetMapping, @PostMapping 등과 같은 Spring MVC 어노테이션을 사용하여 HTTP 요청을 매핑합니다.
Ribbon이란❓
- Ribbon
- Neflix가 개발한 Ribbon은 클라이언트 사이드에서 로드 밸런싱을 지원하는 도구로, 서비스 인스턴스 간의 부하를 효과적으로 분산시킬 수 있습니다.
- 주요 특징
- 서버 리스트 제공자(Server List Provider)
- Ribbon은 Eureka와 같은 서비스 디스커버리 서버로부터 서비스 인스턴스의 리스트를 제공받습니다.
- 이를 통해 서비스 인스턴스의 위치와 상태를 동적으로 업데이트하며, 로드 밸런싱에 필요한 최신 정보를 유지합니다.
- 로드밸런싱 알고리즘 (Load Balancing Algorithms)
- Ribbon은 다양한 로드 밸런싱 알고리즘을 지원합니다.
- 대표적인 알고리즘으로는 라운드 로빈(Round Robin)과 가중치 기반(Weighted) 로드 밸런싱이 있습니다.
- 라운드 로빈 ➡️ 각서버에 요청을 순서대로 분산시키는 방식
- 가중치 기반 ➡️ 각 인스턴스의 처리 능력에 따라 트래픽을 분산시키는 방식
- Failover
- Ribbon은 요청 실패 시 자동으로 다른 서비스 인스턴스로 전환하는 Failover 기능을 제공합니다.
- 만약 특정 인스턴스가
응답하지 않거나 오류가 발생하면, Ribbon은 다른 가용 인스턴스를 선택하여 요청을 처리합니다.
- 서버 리스트 제공자(Server List Provider)
FeignClient와 Ribbon 설정
FeignClient와 Ribbon을 사용하여 서비스 호출과 로드 밸런싱을 설정하려면, Spring Boot 애플리케이션에 몇 가지 의존성과 설정이 필요합니다.
1. Gradle 의존성 추가
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
}
- FeignClient와 Ribbon을 사용할 수 있도록 build.gradle 파일에 위와 같은 의존성을 추가합니다.
2. Spring Boot 애플리케이션 설정
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class MyServiceApplication {
public static void main(String[] args) {
SpringApplication.run(MyServiceApplication.class, args);
}
}
FeignClient와 Ribbon을 사용하기 위해 Spring Boot 애플리케이션에 위와 같은 설정을 추가합니다.
- @SpringBootApplication
- Spring Boot 애플리케이션의 메인 클래스에 붙이는 어노테이션으로, 기본적인 설정과 구성을 자동으로 처리합니다.
- @EnableFeignClients
- FeignClient를 활성화하여 서비스 호출을 위한 인터페이스를 스캔하고 Bean으로 등록합니다.
3. FeignClient 인터페이스 작성
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = "my-service")
public interface MyServiceClient {
@GetMapping("/endpoint")
String getResponse(@RequestParam(name = "param") String param);
}
@FeignClient 어노테이션을 사용하여 호출할 서비스의 인터페이스를 정의합니다.
- @FeignClient(name = "my-service")
- FeignClient가 호출할 서비스의 이름을 지정합니다.
- 이 이름은 Eureka 서버에 등록된 서비스 이름과 일치해야 합니다.
- @GetMapping("/endpoint")
- 호출할 서비스의 엔드포인트를 지정합니다.
따라서 최종 url은 http://my-service/endpoint? parma형태로 구성됩니다.
서버등록 관련 포스팅 ▼
FeignClient와 Eureka 연동
1. 의존성 추가
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
implementation 'org.springframework.cloud:spring-cloud-starter-eureka'
}
2. application.properties 설정
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
위 설정들을 통해 FeignClient가 Eureka 서버를 통해 서비스를 찾을 수 있도록 합니다.
3. FeignClient 인터페이스 정의
@FeignClient(name = "my-service")
public interface MyServiceClient {
@GetMapping("/api/data")
String getData();
}
@FeignClient 어노테이션을 사용하여 호출할 서비스의 인터페이스를 정의합니다.
위 코드에서는
- @FeignClient(name = "my-service")는 my-service라는 이름의 서비스 인스턴스를 호출할 것임을 명시합니다.
- @GetMapping("/api/data")는 해당 서비스의 /api/data 엔드포인트에 GET 요청을 보냅니다.
4. FeignClient 활성화
@SpringBootApplication
@EnableFeignClients
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
애플리케이션에 @EnableFeignClients 어노테이션을 추가하여 FeignClient를 활성화합니다.
@EnableFeignClients 어노테이션은 FeignClient 인터페이스를 스캔하여 Bean으로 등록합니다.
5. 서비스 호출
@RestController
public class MyFeignClientController {
@Autowired
private MyServiceClient myServiceClient;
@GetMapping("/get-data-feign")
public String getDataWithFeignClient() {
return myServiceClient.getData();
}
}
- MyServiceClient 인터페이스를 주입(DI) 받아 getDataWithFeignClient 메서드에서 myServiceClient.getData()를 호출합니다.
- FeignClient는 서비스 이름(my-service)을 사용하여 Eureka 서버에서 서비스 인스턴스를 조회하고, 로드 밸런싱을 통해 적절한 인스턴스에 요청을 전송합니다.
FeignClient와 Ribbon 동작 원리
- 서비스 인스턴스 등록
- my-service라는 이름을 가진 서비스 인스턴스가 Eureka 서버에 등록되어 있어야 합니다.
- 서비스 조회
- FeignClient를 사용할 때, Ribbon은 자동으로 활성화됩니다. 따라서 FeignClient가 호출될 때, my-service라는 이름을 기반으로 Eureka 서버에서 서비스 인스턴스를 조회합니다.
- 로드 밸런싱
- FeignClient는 Ribbon과 통합되어 있어, 여러 인스턴스가 있을 경우 로드 밸런싱을 통해 적절한 인스턴스에 요청을 분산합니다.
- HTTP 요청 전송
- 실제 HTTP 요청은 http://my-service/api/data URL로 전송됩니다.
- 이 URL은 FeignClient가 내부적으로 Eureka 서버에서 서비스 인스턴스를 찾은 후 생성합니다.
로드 밸런싱 동작 확인
Eureka Server에 주문(Order) 서비스 인스턴스 1개와 동일한 기능을 가진 상품 서비스 인스턴스(Product) 3개를 포트만 다르게 설정하여 연결합니다.
사용자가 상품을 요청할 때 (http://localhost:19091/order/1), 응답을 제공하는 상품 인스턴스의 포트를 반환받습니다. 이를 통해 요청이 라운드로빈 방식으로 로드밸런싱 되는 것을 확인해 보겠습니다.
Product instance
Product 인스턴스의 역할
- 상품 정보 제공
- 각 Product 인스턴스는 특정 포트에서 실행되며, 상품에 대한 정보를 제공하는 REST API를 제공합니다.
- 예를 들어, GET /product/{id} 엔드포인트를 통해 상품 ID에 대한 정보를 반환합니다.
- 서비스 디스커버리 등록
- Product 인스턴스는 Eureka 서버에 자신의 존재를 등록합니다.
- Eureka 서버는 이러한 인스턴스를 관리하고, 클라이언트 애플리케이션이 서비스를 발견할 수 있도록 합니다.
- 서비스 등록 시 product-service라는 이름으로 등록되며, 각 인스턴스는 고유의 포트(예: 19092, 19093, 19094)에서 실행됩니다.
- 로드 밸런싱
- 여러 Product 인스턴스가 동일한 서비스 이름(product-service)으로 등록되어 있으면, 클라이언트 애플리케이션에서 요청할 때 라운드로빈 로드밸런싱이 적용됩니다.
- 이는 Order 서비스가 요청을 보낼 때, FeignClient와 Ribbon이 함께 동작하여 요청을 각 Product 인스턴스에 균등하게 요청을 분산시킵니다.
Order instance
- 클라이언트 요청
- 클라이언트가 http://localhost:19091/order/1와 같은 URL로 HTTP GET 요청을 보냅니다.
- OrderController에서 요청 처리
- OrderController의 getOrder 메서드가 호출됩니다.
- 이 메서드는 orderId를 매개변수로 받아옵니다.
- OrderService 호출
- OrderController는 OrderService의 getOrder 메서드를 호출합니다.
- 이 메서드는 주문 ID를 바탕으로 상품 정보를 얻기 위해 ProductClient를 사용합니다.
- FeignClient를 통한 외부 서비스 호출
- ProductClient는 Feign을 사용해 product-service라는 이름의 서비스를 호출합니다.
- 이 호출은 FeignClient 인터페이스에 정의된 getProduct 메서드를 통해 이루어집니다.
- Feign은 자동으로 product-service 인스턴스 중 하나를 선택하고, 해당 인스턴스의 엔드포인트(/product/{id})에 요청을 보냅니다.
- Product 서비스에서 응답받기
- Product 서비스는 요청을 받아 상품 정보를 포함한 응답을 반환합니다.
- Feign은 이 응답을 OrderService로 전달합니다.
- 서비스 결과 조합 및 반환
- OrderService는 Product 서비스로부터 받은 상품 정보를 바탕으로 최종 응답을 조합합니다.
- OrderController는 이 응답을 클라이언트에게 반환합니다.
- 결과 응답
- 클라이언트는 OrderController에서 반환된 결과를 받아 볼 수 있습니다.
Order 인스턴스의 역할
- 주문 정보 제공
- Order 인스턴스는 주문에 대한 정보를 제공하는 REST API를 제공합니다.
- 엔드포인트를 통해 특정 주문 ID에 대한 정보를 반환합니다.
- 상품 정보 요청
- Order 인스턴스는 Product 인스턴스와 통신하여 상품 정보를 조회합니다. 이를 위해 FeignClient를 사용하여 Product 서비스의 엔드포인트에 요청을 보냅니다.
- OrderService 클래스는 ProductClient를 통해 Product 인스턴스의 GET /product/{id} 엔드포인트를 호출하고, 상품 정보를 받아옵니다.
- 서비스 디스커버리 등록
- Order 인스턴스는 Eureka 서버에 자신의 존재를 등록합니다. 이를 통해 Order 서비스는 다른 서비스들에 의해 발견되고, 다른 서비스들이 Order 서비스의 인스턴스를 조회할 수 있게 됩니다.
- Order 인스턴스는 order-service라는 이름으로 등록됩니다.
- 라우팅 및 로드 밸런싱
- 클라이언트가 Order 서비스의 엔드포인트에 요청을 보내면, Order 인스턴스는 이 요청을 처리하고 필요한 경우 Product 서비스에 대한 요청을 수행합니다.
- Order 인스턴스는 로드 밸런싱이 가능하며, 여러 인스턴스가 존재할 경우 클라이언트 요청이 라운드로빈 방식으로 분산될 수 있습니다.
전체 동작 흐름
- Eureka 서버 실행
- Eureka 서버를 실행하여 서비스 디스커버리 기능을 제공하는 중앙 서버를 준비합니다.
- Order 서비스 실행
- Order 애플리케이션을 실행하여 주문 관련 API를 제공하는 서비스를 시작합니다.
- Order 애플리케이션은 OrderService를 통해 ProductClient를 사용하여 상품 정보를 가져옵니다.
- Product 서비스 실행
- Product 애플리케이션을 3개의 포트(19092, 19093, 19094)에서 실행하여 동일한 상품 정보를 제공하는 여러 인스턴스를 실행합니다.
- 각 Product 인스턴스는 자신의 포트를 반환하는 API를 제공합니다.
- Eureka 대시보드 확인
- http://localhost:19090/ 에 접속하여 Eureka 대시보드에서 등록된 서비스를 확인합니다.
- 여기서 product-service라는 이름으로 등록된 인스턴스 3개(포트 19092, 19093, 19094)를 확인할 수 있습니다.
http://localhost:19091/order/1에 접속하면 Order 서비스가 product-service에 요청을 보냅니다.
FeignClient를 사용하여 Product 서비스에 요청을 보내면, Eureka와 Ribbon이 라운드로빈 방식으로 여러 Product 인스턴스 중 하나를 선택하여 응답합니다.
위와 같이19092 ➡️ 19093 ➡️ 19094로 라운드 로빈방식이기 때문에 순서대로 가져오는 것을 확인할 수 있습니다.
또한 요청할 때마다 응답의 포트 번호가 변경되는 것을 확인할 수 있습니다. 이는 요청이 여러 Product 인스턴스에 분산되어 처리되기 때문입니다.
만약 위 경우처럼 orderId가 11일 때에는 getProductInfo() 메서드가 호출되지 않으며, FeignClient를 통한 ProductApplication 호출이 일어나지 않습니다. 클라이언트는 "Not exist order..."라는 응답을 받게 됩니다.
'Framework > Spring\Spring boot' 카테고리의 다른 글
[Spring Cloud] Spring Cloud Gateway로 MSA 환경에서의 효율적인 요청 처리와 로드 밸런싱 구현하기 (0) | 2024.08.03 |
---|---|
[Spring Cloud] Resilience4j를 활용한 서킷 브레이커의 상태 변화와 Fallback 메커니즘 (0) | 2024.08.03 |
[Spring Cloud] Eureka 서버 및 Eureka클라이언트 설정과 서비스 디스커버리(FeignClient) (1) | 2024.08.01 |
[Spring MVC] Filter: 웹 애플리케이션 보안을 위한 필터의 역할과 활용법 (0) | 2024.08.01 |
[Spring Cloud] Spring Cloud란 무엇일까 ❓ (마이크로서비스 아키텍처(MSA)를 위한 프레임워크 ) (0) | 2024.07.31 |