Feign 클라이언트 구현과 Eureka, Hystrix 적용

최호승·2022년 3월 27일
0
post-custom-banner

1. Dependency

프로젝트 의존성에 spring-cloud-starter-openfeign를 추가한다.

implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

2. Feign client 활성화

@EnableFeignClients 애노테이션을 추가하여 feign client를 활성화한다.

@SpringBootApplication 또는 @Configuration 클래스에 추가하면 된다.

@EnableFeignClients
@SpringBootApplication
publicclassApplication {
publicstaticvoidmain(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3. Feign 인터페이스 개발

Feign 클라이언트는 인터페이스에 @FeignClient를 붙이는 것만으로 만들 수 있다.

다음은 feign 클라이언트의 예이다.

@FeignClient(name = "coupon", url = "${api-url}")
publicinterfaceCouponClient {

    @GetMapping("/coupon/v1/coupons/infos")
    List<Coupon>infos(@RequestParam("couponNos") List<Long> couponNos);

    @GetMapping("/coupon/v1/issue/status/{memberNo}")
    CouponIssueStatusInfoissueStatusInfo(@PathVariable(value = "memberNo") String memberNo,
                                          @RequestParam(value = "couponNo") Long couponNo);

    @GetMapping("/coupon/v1/issued/status/{memberNo}")
    List<CouponIssueInfo>issueInfos(@PathVariable("memberNo") String memberNo,
                                     @RequestParam("couponNos") List<Long> couponNos);
}

Spring mvc의 handler mapping 애노테이션을 사용해서 핸들러를 정의한다.

사용된 @FeignClient 속성에 대해 알아보자.

name

name은 필수 속성으로 서비스 ID 또는 서비스의 논리적인 이름이다. 서비스 디스커버리에서 사용된다.

url

url은 feign client를 통해 호출할 url이다. eureka, ribbon, hystrix를 사용하지 않을 때 설정한다. 예를 들어 http://api.atoz-develop.com:8765 와 같이 설정한다. profile 별로 다른 url을 사용하려면 위 코드와 같이 설정으로 빼면 된다.

4. Feign Client 사용

작성한 feign client는 빈을 주입받아서 사용할 수 있다.

@Slf4j
@RequiredArgsConstructor
@Service
publicclassDefaultInquireCouponServiceimplementsInquireCouponService {

privatefinal CouponClient couponClient;

    @Override
public List<Coupon>getCouponInfos(String couponNumbers) {

try {
            List<Coupon> couponInfoList = couponClient.infos(couponNos);
            ...
        }catch (Exception ex) {
            log.error("##getCouponInfos ERR:", ex);
        }

return couponInfos;
    }
}

5. Eureka(유레카) 적용

위에서는 @FeignClient의 url 속성에 호출할 대상을 직접 지정했다.

이번에는 eureka를 사용해서 서비스 디스커버리를 적용해보자.

유레카 서버는 준비된 것으로 가정한다.

dependency

eureka client 의존성을 추가한다.

implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'

Eureka Client 활성화

@EnableEurekaClient를 추가하여 eureka client를 활성화한다.

@EnableFeignClients
@EnableEurekaClient
@SpringBootApplication
publicclassApplication {
publicstaticvoidmain(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

설정

디스커버리 설정을 추가한다.

eureka.client.serviceUrl.defaultZone에 유레카 서버 url을 설정하면 된다.

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

추가로 유레카 서버에 현재 클라이언트 서비스를 등록하지 않으려면 register-with-eureka를 false로 설정한다.

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://localhost:8761/eureka/

Feign Client 수정

이제 작성한 feign client에서 @FeignClient의 url 속성을 제거한다.

@FeignClient(name = "coupon")
publicinterfaceCouponClient {

    ...
}

서비스 기동 후 응답이 정상적으로 오면 서비스 디스커버리가 잘 적용된 것이다.

추가로 feign client는 다음과 같이 timeout 및 로그 레벨을 설정할 수 있다.

feign:
  client:
    config:
default:
        loggerLevel: BASIC
        connectTimeout: 20000
        readTimeout: 20000

connectTimeout은 서비스 접속 타임아웃, readTimeout은 HTTP 연결 읽기 타임아웃 설정이다.

default 대신 특정 서비스에 대해 설정할 수도 있다.

6. hystrix 적용

최신 버전의 스프링 클라우드에서는 feign의 hystrix 활성화가 disable 되어있기 때문에 활성화하기 위해 다음 설정을 추가해야 한다.

feign:
  hystrix:
    enabled: true

feign client에서 대상 서비스로의 요청 실패가 임계치를 넘으면 대신 응답할 FallBackFactory 클래스를 설정할 수 있다. 참고로 이러한 매커니즘을 Circuit breaker pattern이라고 하며 서킷이 open이면 오류 상태, close이면 정상 상태를 나타낸다.

아래와 같이 FallbackFactory를 implements하여 구현한다.

@Slf4j
@Component
publicclassCouponClientFallbackFactoryimplementsFallbackFactory<CouponClient> {

    @Override
public CouponClientcreate(Throwable cause) {

returnnewCouponClient() {

            @Override
public List<Coupon>infos(List<Long> couponNos) {
                log.error("couponNos: {}", couponNos, cause);
return Collections.emptyList();
            }

            @Override
public CouponIssueStatusInfoissueStatusInfo(String memberNo, Long couponNo) {
                log.error("memberNo: {}, couponNo: {}", memberNo, couponNo, cause);
return null;
            }

            @Override
public List<CouponIssueInfo>issueInfos(String memberNo, List<Long> couponNos) {
                log.error("memberNo: {}, couponNos: {}", memberNo, couponNos, cause);
return Collections.emptyList();
            }
        };
    }
}

다음과 같이 @FeignClient에 fallbackFactory 속성값을 추가한다.

@FeignClient(name = "coupon", fallbackFactory = CouponClientFallbackFactory.class)
publicinterfaceCouponClient {

    ...
}

6-1. Hystrix 설정

Hystrix를 java bean으로 다음과 같이 설정할 수 있다.

참고로 spring-cloud-netflix-hystrix 2.2.6.RELEASE 기준이다.

@Bean
public Customizer<HystrixCircuitBreakerFactory>defaultConfig() {
return factory -> factory.configureDefault(id -> HystrixCommand.Setter
            .withGroupKey(HystrixCommandGroupKey.Factory.asKey(id))
            .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                    .withExecutionTimeoutInMilliseconds(20000)
                    .withCircuitBreakerRequestVolumeThreshold(20)
                    .withCircuitBreakerErrorThresholdPercentage(50)
                    .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD))
            .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
                    .withCoreSize(50)
                    .withMaximumSize(300)
                    .withAllowMaximumSizeToDivergeFromCoreSize(true))
    );
}

공식 reference: https://docs.spring.io/spring-cloud-netflix/docs/2.2.6.RELEASE/reference/html/#configuring-hystrix-circuit-breakers

출처

profile
백엔드 개발자
post-custom-banner

0개의 댓글