Spring Cloud

4. Feign - [Spring Cloud를 활용한 MSA 기초 온라인 강의 실습]

Interface 선언을 통해 자동으로 Http Client를 생성

기존 RestTemplate은 concreate 클래스라 테스트가 어려움

관심사 분리

 - 서비스의 관심: 다른 리소스, 외부 서비스 호출과 리턴값

 - 관심x : 어떤 URL, 어떻게 파싱할 것인가

 

참조하면 좋은 사이트

우형 기술 블로그

https://woowabros.github.io/experience/2019/05/29/feign.html

공식 문서

https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html#_creating_feign_clients_manually

실습

1. 의존성

ext['springCloudVersion'] = 'Finchley.SR2'

...

dependencies {
    compile('org.springframework.cloud:spring-cloud-starter-openfeign')  // To use Feign
    ...
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

2. 설정

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class DisplayApplication {

    public static void main(String[] args) {
        SpringApplication.run(DisplayApplication.class);
    }

}

 

3. FeignClient 인터페이스 구현

// @FeignClient(name = "product", url = "http://localhost:8082/") // 유레카 미 사용시
@FeignClient(name = "product")
public interface FeignProductRemoteService {
    @RequestMapping(path = "/products/{productId}")
    String getProductInfo(@PathVariable("productId") String productId);
}

 

4. 인터페이스 사용

@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");
    }
}

 

5. hystrix 설정을 - Feign 의 모든 요청에 hystrix가 적용됨

feign:
  hystrix:
    enabled: true

 

6-1. fallback

@FeignClient(name = "product", fallback = FeignProductRemoteServiceFallbackImpl.class)
public interface FeignProductRemoteService {
    @RequestMapping(path = "/products/{productId}")
    String getProductInfo(@PathVariable("productId") String productId);
}
@Component
public class FeignProductRemoteServiceFallbackImpl implements FeignProductRemoteService {
    @Override
    public String getProductInfo(String productId) {
        return "[ this product is sold out ]";
    }
}

 

6-2. FallbackFactory 

@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 ]";
    }
}
@FeignClient(name = "product", fallbackFactory = FeignProductRemoteServiceFallbackFactory.class)
public interface FeignProductRemoteService {
    @RequestMapping(path = "/products/{productId}")
    String getProductInfo(@PathVariable("productId") String productId);
}

 

7. 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