티스토리 뷰
Feign은 넷플릭스에서 개발된 Http client binder이다
사용하기 위해서는 interface를 작성하고 어노테이션을 선언하기만 하면된다
마치 spring data jpa에서 실제 쿼리를 작성하지 않고 interface만 지정하여 쿼리 실행 구현체를 자동으로 만들어주는 것과 유사하다
이번 시간에는 RestTemplate이 아닌 Feign을 사용하여 외부 API를 호출하는 실습을 해보도록하겠다
실습해보기
1. 라이브러리 의존성 추가
compile('org.springframework.cloud:spring-cloud-starter-openfeign') // To use Feign
2. 메인 클래스에 @EnableFeignClients 어노테이션 추가
@SpringBootApplication
@EnableFeignClients
public class DisplayApplication {
public static void main(String[] args) {
SpringApplication.run(DisplayApplication.class);
}
}
3.FeignProductRemoteService라는 인페이스 생성
@FeignClient(name = "product", url="localhost:8082/")
public interface FeignProductRemoteService {
@RequestMapping(path = "/products/{productId}")
String getProductInfo(@PathVariable("productId") String productId);
}
- @FeingClient name - 요청보내는 서비스 이름 , url - 요청 보내는 서비스의 url
- MVC 어노테이션을 사용하여 요청부분 작성
4. Controller에서 인터페이스 사용하기
@RestController
@RequestMapping(path = "/displays")
public class DisplayController {
private final FeignProductRemoteService feignProductRemoteService;
public DisplayController(FeignProductRemoteService feignProductRemoteService) {
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");
}
}
- 선언했던 인터페이스를 컨트롤러에서 주입받아 사용한다
5. RestTemplate을 사용한 코드와의 비교
RestTemplate을 사용한 코드
@Service
public class ProductRemoteServiceImpl implements ProductRemoteService {
private static final String url = "http://product/products/";
private final RestTemplate restTemplate;
public ProductRemoteServiceImpl(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@Override
@HystrixCommand(commandKey = "productInfo", fallbackMethod = "getProductInfoFallback")
public String getProductInfo(String productId) {
return this.restTemplate.getForObject(url + productId, String.class);
}
public String getProductInfoFallback(String productId, Throwable t) {
System.out.println("t = " + t);
return "[ this product is sold out ]";
}
}
위의 FeignProductRemoteService 인터페이스와 비교해보면 코드양의 차이를 느낄 수 있다
Feign + Eureka,Ribbon,Hystrix 모드로 사용하기
인터페이스에서 URL 제거
@FeignClient(name = "product")
public interface FeignProductRemoteService {
@RequestMapping(path = "/products/{productId}")
String getProductInfo(@PathVariable("productId") String productId);
}
- 이렇게 URL을 명시하지 않고 Eureka와 함께 쓰게된다면 Ribbon + Hystrix 도 내부적으로 실행시켜준다
즉 @FeignClient에 URL 명시 시
순수 Feign Client로서만 동작 (Http Client의 역할만을 수행)
@FeignClient에 URL을 명시하지 않는다면
Feign + Ribbon + Eureka 모드로 동작
- 어떤 서버 호출 ? name="product"
- eureka에서 product 서버 목록을 조회해서 ribbon을 통해 load-balacing하면서 HTTP 호출을 수행
단 Feign + Hystrix,Ribbon,Eureka 모드를 사용하려면 application.yml에서 Hystrix에 대한 설정 필요
feign:
hystrix:
enabled: true
Feign용 Hystrix Fallback 설정하기
방법 1 - Feign으로 정의한 인터페이스에 직접 Fallback 메소드를 직접 구현하고 Spring Bean으로 선언
@Component
public class FeignProductRemoteServiceFallbackImpl implements FeignProductRemoteService {
@Override
public String getProductInfo(String productId) {
return "[ this product is sold out ]";
}
}
- 인터페이스의 메소드를 오버라이드하여 Fallback 메소드를 구현한다
Fallback 클래스를 @Feign선언 시 명시
@FeignClient(name = "product", fallbackFactory = FeignProductRemoteServiceFallbackImpl.class)
public interface FeignProductRemoteService {
@RequestMapping(path = "/products/{productId}")
String getProductInfo(@PathVariable("productId") String productId);
}
이렇게 하면 Fallback 메소드를 개발자가 설정할 수 있지만 Throwable 파라미터 사용을 할 수 없으므로 정확한 에러의 원인을 알 수 없다
에러의 원인까지 알고 싶다면 FallbackFactory를 사용해야한다
방법 2 - FallbackFactory의 create 메소드를 구현
@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 ]";
}
}
- Throwable 파라미터로 에러원인을 확인할 수 있다
FallbackFactory 클래스를 @Feign선언 시 명시
@FeignClient(name = "product", fallbackFactory = FeignProductRemoteServiceFallbackFactory.class)
public interface FeignProductRemoteService {
@RequestMapping(path = "/products/{productId}")
String getProductInfo(@PathVariable("productId") String productId);
}
Feign용 Hystrix 설정
hystrix:
command:
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
- Command Key이름 주의
- FeignProductRemoteService#getProductInfo(String)
마무리
Feign은 인터페이스 선언 + 설정으로 다음과 같은 것들이 가능하다
- Http Client - 외부 API 호출
- Eureka 타켓 서버 주소 획득
- Ribbon을 통한 Client-Side Load Balancing
- Hystrix를 통한 메소드별 Circuit Breaker
강의중에 동작 순서에 대해 잘 표현된 그림이 있어서 참고용으로 올립니당
'BackEnd > Spring MSA' 카테고리의 다른 글
Netflix Zuul의 동작 과정과 Filter 설명 (1) | 2020.10.20 |
---|---|
[Spring cloud 기반 MSA 맛보기] - Zuul을 사용해보자 (0) | 2020.09.08 |
[Spring cloud 기반 MSA 맛보기] - Eureka를 사용해보자 (0) | 2020.09.07 |
[Spring cloud 기반 MSA 맛보기] - Ribbon을 사용해보자 (0) | 2020.09.07 |
[Spring cloud 기반 MSA 맛보기] - Hystrix를 사용해보자 (0) | 2020.09.07 |