티스토리 뷰

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 

강의중에 동작 순서에 대해 잘 표현된 그림이 있어서 참고용으로 올립니당

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함