728x90
7. Declarative Http Client - Feign
- Interface 선언을 통해 자동으로 Http Client 를 생성
- RestTemplate 은 concreate 클래스라 테스트하기 어렵다
- 관심사의 분리
- 서비스의 관심 - 다른 리소스, 외부 서비스 호출과 리턴값
- 관심 X - 어떤 URL, 어떻게 파싱할 것인가
- Spring Cloud 에서 Open-Feign 기반으로 Wrapping 한 것이 Spring Cloud Feign
Declarative Http Client - Spring Cloud Feign
인터페이스 선언 만으로 Http Client 구현물을 만들어 줌
@FeignClient(name = "db", url="http://localhost:8080/")
public interface ProductResource {
@RequestMapping(path = "/query/{itemId}")
String getItemDetail(@PathVariable("itemId") String itemId);
}
@FeignClient
를 Interface에 명시- 각 API를 Spring MVC Annotation을 이용해서 정의
How to Use
@Autowired
ProductResource productResouce;
@Autowired
를 통해 DI 받아 사용
[실습 Step-5] Feign 클라이언트 사용하기
Tag : step-5-feign-url
1. 목적
Display -> Product 호출시에 Feign을 사용해서 RestTemplate을 대체해 보려고 함
2. [display] build.gradle에 dependency 추가
- compile('org.springframework.cloud:spring-cloud-starter-openfeign')
3. [display] DisplayApplication에 @EnableFeignClients
추가
@SpringBootApplication
@EnableCircuitBreaker
@EnableEurekaClient
@EnableFeignClients
public class DisplayApplication {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(DisplayApplication.class);
}
}
4. [display] Feign용 Interface 추가
- 이전
ProductRemoteService
사용하지 않고 데모를 위해 별도 정의
package com.elevenst.service;
public interface FeignProductRemoteService {
String getProductInfo(String productId);
}
5. [display] FeignProductRemoteService에 Annotation 넣기
package com.elevenst.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(name = "product", url = "http://localhost:8082/")
public interface FeignProductRemoteService {
@RequestMapping(path = "/products/{productId}")
String getProductInfo(@PathVariable("productId") String productId);
}
- Client 구현 완성. 서버 가동시
FeignProductRemoteService
용 Spring Bean 자동 생성FeignProductRemoteService
를 주입 받아서 사용하면 됨
6. [display] DisplayController에서 호출 부분 변경
@RestController
@RequestMapping(path = "/displays")
public class DisplayController {
private final ProductRemoteService productRemoteService;
private final FeignProductRemoteService feignProductRemoteService;
public DisplayController(ProductRemoteService productRemoteService,
FeignProductRemoteService feignProductRemoteService) {
this.productRemoteService = productRemoteService;
this.feignProductRemoteService = feignProductRemoteService;
}
@GetMapping(path = "/{displayId}")
public String getDisplayDetail(@PathVariable String displayId) {
String productInfo = getProductInfo();
return String.format("[display id = %s at %s %s ]", displayId, System.currentTimeMillis(), productInfo);
}
private String getProductInfo() {
return feignProductRemoteService.getProductInfo("12345");
}
}
기존 RestTemplate사용했던 서비스 대신 Feign이 자동으로 생성해준 서비스 빈을 사용
7. 동작 확인
http://localhost:8081/displays/11111
8. 정리
- Feign은 Interface 선언을 통해 자동으로 HTTP Client를 만들어주는
Declarative Http Client - Open-Feign 기반으로 Spring Cloud가 Wrapping한것이 오늘 실습한
Spring Cloud Feign - 방금 한 실습 (Feign + URL 명시)은
- No Ribbon
- No Eureka
- No Hystrix
[실습 Step-5] Feign + Hystrix,Ribbon,Eureka
Tag : step-5-feign-eureka
1. 배경
Feign의 또 다른 강점은 Ribbon + Eureka + Hystrix와 통합되어 있 다는 점
2. [display] Feign에 Eureka + Ribbon 적용하기
@FeignClient(name = "product")
public interface FeignProductRemoteService {
@RequestMapping(path = "/products/{productId}")
String getProductInfo(@PathVariable("productId") String productId);
}
@FeignClient
에서 url만 제거
완성 !!!
3. Feign의 동작
@FeignClient
에 URL 명시 시- 순수 Feign Client로서만 동작
@FeignClient
에 URL 명시 하지 않으면 ?- Feign + Ribbon + Eureka 모드로 동작
- 어떤 서버 호출하나 ?
@FeignClient(name='product')
- 즉, eureka에서 product 서버 목록을 조회해서 ribbon을 통해
load-balancing하면서 HTTP 호출을 수행
4. [display] application.yml : Feign + Hystrix
Tag : step-5-feign-hystrix
feign:
hystrix:
enabled: true
위의 설정이 들어가면 메소드 하나 하나가 Hystrix Command 로서 호출됨
5. Feign에서 Fallback은 ? Hystrix 설정은 ?
Declarative Http Client - Spring Cloud Feign
그럼 Hystrix Fallback은 ??
- Feign으로 정의한 Interface를 직접 구현 하고 Spring Bean으로 선언
@Component
public class ProductResourceFallback implements ProductResource {
@Override
public String getItemDetail(String itemId) {
return 'default value';
}
}
- Fallback 클래스를
@Feign
선언시 명시
@FeignClient(fallback=ProductResourceFallback.class, name = "db", url="http://localhost:8080/")
public interface ProductResource {
@RequestMapping(value = "/query/{itemId}", method=RequestMethod.GET)
String getItemDetail(@PathVariable("itemId") String itemId);
}
6. [display] Feign용 Hystrix Fallback 선언 - Spring Bean으로 정의
@Component
public class FeignProductRemoteServiceFallbackImpl implements FeignProductRemoteService {
@Override
public String getProductInfo(String productId) {
return "[ this product is sold out ]";
}
}
7. [display] Feign용 Hystrix Fallback 명시
@FeignClient(name = "product", fallback = FeignProductRemoteServiceFallbackImpl.class)
public interface FeignProductRemoteService {
@RequestMapping(path = "/products/{productId}")
String getProductInfo(@PathVariable("productId") String productId);
}
@FeignClient
에 fallback
속성으로 클래스 명시
8. [display] 기본 Fallback 은 에러 원인(Exception) 을 알 수 없음 - FallbackFactory 사용
package com.elevenst.service;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
@Component
public class FeignProductRemoteServiceFallbackFactory implements FallbackFactory<FeignProductRemoteService> {
@Override
public FeignProductRemoteService create(Throwable cause) {
System.out.println("t = " + cause);
return productId -> "[ this product is sold out ]";
}
}
9. [display] Feign용 Hystrix Fallback 명시
@FeignClient(name = "product", fallback = FeignProductRemoteServiceFallbackImpl.class)
public interface FeignProductRemoteService {
@RequestMapping(path = "/products/{productId}")
String getProductInfo(@PathVariable("productId") String productId);
}
@FeignClient
에 fallback
속성으로 클래스 명시
10. [display] Feign용 Hystrix 프로퍼티 정의하는 법
hystrix:
command:
productInfo: # command key. use 'default' for global setting.
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
circuitBreaker:
requestVolumeThreshold: 1 # Minimum number of request to calculate circuit breaker's health. default 20
errorThresholdPercentage: 50 # Error percentage to open circuit. default 50
FeignProductRemoteService#getProductInfo(String):
execution:
isolation:
thread:
timeoutInMilliseconds: 3000 # default 1,000ms
circuitBreaker:
requestVolumeThreshold: 1 # Minimum number of request to calculate circuit breaker's health. default 20
errorThresholdPercentage: 50 # Error percentage to open circuit. default 50
Feign 사용하는 경우 commandKey 이름 주의
ex) FeignProductRemoteService#getProductInfo(String)
정리
Feign은 인터페이스 선언 + 설정으로 다음과 같은 것들이 가능하다
- Http Client
- Eureka 타겟 서버 주소 획득
- Ribbon을 통한 Client-Side Load Balancing
- Hystrix를 통한 메소드별 Circuit Breaker
장애 유형 별 동작 예
- 특정 API 서버의 인스턴스가 한개 Down 된 경우
- EUREKA - Heartbeat 송신이 중단 됨으로 일정 시간 후 목록에서 사라짐
- RIBBON - IOException 이 발생한 경우 다른 인스턴스로
Retry - Hystrix - Circuit 은 오픈되지 않음( ERROR = 33% ) Fallback, Timeout 은 동작
- 특정 API 가 비정상 동작하는 경우 (지연, 에러)
- Hystrix - 해당 API 를 호출하는 Circuit Breaker 오픈 Fallback, Timeout 도 동작
728x90
'SpringCloud' 카테고리의 다른 글
[Spring Cloud 를 활용한 MSA 기초] 9. Configuration Server, Tracing.Monitaring, 그리고 남은 이야기 (0) | 2021.02.07 |
---|---|
[Spring Cloud 를 활용한 MSA 기초] 8. API Gateway - Zuul (0) | 2021.02.07 |
[Spring Cloud 를 활용한 MSA 기초] 6. Service Registry - Eureka (0) | 2021.02.07 |
[Spring Cloud 를 활용한 MSA 기초] 5. Client LoadBalancer - Ribbon (0) | 2021.02.07 |
[Spring Cloud 를 활용한 MSA 기초] 4. Circuit Breaker - Hystrix (0) | 2021.02.07 |
댓글