기록해야 기억한다/Spring

Spring Cloud OpenFeign, 그리고 SSL

bj-lee 2020. 12. 29. 00:05

OpenFeign (github.com/OpenFeign/feign)

Feign 은 선언적 Web Service Client 인 오픈소스이다. 이를 이용해 우리가 Spring 에서 Service 를 DI 를 통해 호출하여 사용하는 것처럼 웹서비스 Client 를 사용할 수 있게 도와준다. 

 

Feign 은 Interface 를 만들고 annotation 을 추가하는것으로 쉽게 사용할 수 있고, encoder, decoder 를 플러그형으로 지원한다.

 

Spring Cloud OpenFeign

Spring Cloud는 Spring MVC annotation 및 Spring Web에서 기본적으로 사용되는 동일한 HttpMessageConverters를 사용하기위한 지원을 추가하고 Eureka, Spring Cloud LoadBalancer 를 통합하여 Feign 을 사용할 때 부하 분산 된 http 클라이언트를 제공하는 역할을 한다. 물론 Eureka, LoadBalancer 등을 사용하지 않아도 된다.

 

Spring boot 에서의 사용 선언

maven/gradle 에서 spring-cloud-starter-openfeign 선언을 통해 사용할 수 있으며 spring boot 에서의 규칙과 같이 @Enable annotation 을 통해 사용가능하도록 ON 한다.

아래의 코드들은 reference 문서를 이용하거나 간략히 수정한 내용입니다.
@SpringBootApplication
@EnableFeignClients
public class Application {

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

}

StoreClient.java

@FeignClient("stores")
public interface StoreClient {
    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    List<Store> getStores();

    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    Page<Store> getStores(Pageable pageable);

    // or
    @PostMapping("/stores/{storeId}", consumes = "application/json")
    Store update(@PathVariable("storeId") Long storeId, Store store);
}

@FeignClient 는 name, url, configuration 등을 선언할 수 있고, 이 중 name 은 LoadBalancer 클라이언트의 이름으로 사용됩니다. url 은 절대값이나 hostname 만을 사용하여 URL을 지정할 수 있고 Placeholders 를 지원한다.

 

Feign 기본 값의 재정의(Overriding)

Spring Cloud는 FeignClientsConfiguration을 사용하여 각 명명 된 클라이언트에 대한 요구에 따라 ApplicationContext로 새로운 앙상블을 생성하고, @FeignClient 어노테이션의 contextId 속성을 사용하여 해당 앙상블의 이름(Bean name)을 대체 할 수 있다.Configuration part 들은 아래와 같이 구성할 수 있고 해당 속성을 재정의하면 기존 구성을 대체한다.

  • feign.Decoder
  • feign.Encoder
  • feign.Contract
@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
    //..
}
@FeignClient 의 configuration 속성으로 지정된 class 는 Spring Component 인 @Configuration 으로 선언할 필요가 없다. 만약 지정된 경우는 해당 클래스에서 선언된 part 들이 기본 구성이 된다. 만약 선언한다면 @ComponentScan 의 패키지 밖에 선언하거나, 명시적으로 exclude 시켜서 제외할 수 있다.
url 속성을 사용할때는 꼭 name 속성이 필요하다.

 

Spring Cloud OpenFeign 은 기본적으로 다음의 Bean 들을 제공한다.

  • Bean 형태 : bean 이름 : Class 이름
  • Decoder : feignDecoder : ResponseEntityDecoder(SpringDecoder 를 감싼)
  • Encoder : feignEncoder : SpringEncoder
  • Logger : feignLogger : Slf4jLogger
  • Contract : feignContract : SpringMvcContract
  • Feign.Builder : feignBuilder : HystrixFeign.Builder
  • Client : feignClient : Spring Cloud LoadBalancer 가 classpath 에 있으면 FeignBlockingLoadBalancerClient, 없으면 기본 feign client 가 사용된다.

위의 Bean 들은 @Configuration 을 통해 구성할 수 있지만 properties(feign.~~) 을 통해서도 가능하며, @Configuration 과 함께 선언되어 있으면 properties 파일이 우선됩니다.

#application.yml
feign:
  client:
    config:
      feignName:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        errorDecoder: com.example.SimpleErrorDecoder
        retryer: com.example.SimpleRetryer
        requestInterceptors:
          - com.example.FooRequestInterceptor
          - com.example.BarRequestInterceptor
        decode404: false
        encoder: com.example.SimpleEncoder
        decoder: com.example.SimpleDecoder
        contract: com.example.SimpleContract

OkHttpClient, ApacheHttpClient 를 이용해 feign client 를 사용하려면 feign.[client].enabled 속성을 설정(true)하고 classpath 에 추가하면 사용할 수 있다. Apache 를 사용할 경우 org.apache.http.impl.client.CloseableHttpClient, OK HTTP 를 사용할 경우 okhttp3.OkHttpClient 를 사용하며 bean을 제공하여 HTTP Client 를 customize 할 수 있다.

 

경우에 따라 Feign.Builder API 를 통해 Feign 클라이언트를 사용자정의 형식으로 생성할 수 있다.  

 

Feign 에서의 SSL - ignore Cerification

기본적으로 @FeignClient 의 url 속성에 프로토콜을 명시하지 않으면 http 로 요청을 생성하고 필요한 경우 명시적으로 프로토콜을 선언하면 해당 프로토콜로 생성된다.

 

HttpClient, HttpUrlConnection 등의 클래스들을 통해 SSL 을 사용할 때 테스트를 위해서? 혹은 환경적인 요인으로 인해서건 SSL 을 무시해야 할 경우가 있고, Feign 에서의 그 방법을 설명한다.

 

기본적으로 @FeignClient 의 configuration 을 통해 구성을 할 수 있고 해당 구성에서 feign.Client(feignClient) 을 재정의 해서 해결 할 수 있다.

public class SslCertificationIgnoreConfiguration {
    private SSLSocketFactory sslContextFactory() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext ssl_ctx = SSLContext.getInstance("TLS");
        TrustManager[] certs = new TrustManager[]{
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[]{};
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String t) {
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String t) {
                    }
                }};
        ssl_ctx.init(null, certs, new SecureRandom());
        return ssl_ctx.getSocketFactory();
    }

    @Bean
    public feign.Client client() throws KeyManagementException, NoSuchAlgorithmException {
        return new Client.Default(sslContextFactory(), (hostname, session) -> true);
    }
}

 

feign.Client 선언을 통해 Client 빈이 재정의 되며 해당 client 는 재정의된 TrustManager, HostnameVerifier 를 통해 통과되어 진행한다. 단, 해당방법은 인증서 문제등으로 해결할 수 없는 경우에만 사용하는게 바람직하다.

반응형
LIST