如何将自定义 Http 客户端注入 Spring Cloud openfeign?

How to inject custom Http client to Spring Cloud openfeign?

我正在尝试向 Spring Cloud OpenFeign 提供 CloseableHttpClient。 Spring Cloud Open Feign Documentation说它支持 CloeableHttpClient。 Spring 文档没有给出任何实际替换 HTTP 客户端的示例。

基本上,我正在向 HTTP 客户端提供 SSLContext,我希望 Feign 使用这个加载了 SSLContext 的客户端。 如何将此 CloseableHttpClient 注入到 Feign 中?

以下是我的相关配置:

  1. 我正在使用 SpringBootApp
@SpringBootApplication
@EnableFeignClients
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
  1. Feign客户端界面如下:
import org.springframework.cloud.openfeign.FeignClient;
//skipping rest of the imports for brevity
    @FeignClient(name ="remote-service", url = "${remote.service-url}", configuration = FeignConfig.class)
        public interface RemoteServiceApi {
            @GetMapping(value = "/api/v1/resources/{Id}")
            public String getResource(@PathVariable("Id") String Id);
        }
  1. FeignConfig class
import org.apache.http.impl.client.CloseableHttpClient;
//skipping rest of the imports for brevity
public class FeignConfig {
    @Bean
    public CloseableHttpClient client() {
         CloseableHttpClient httpClient=null;
         try {
                    //... Skipping code for brevity.  
                    //here creating "sslSocketFactory" used in the HttpClient builder below
                    httpClient = HttpClients.custom().setSSLSocketFactory(sslSocketFactory)
                    .setMaxConnTotal(10)
                    .setMaxConnPerRoute(10)
                    .build();
                
            }catch(IOException | KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | CertificateException e) {
                System.err.println("Exception during creation of HttpClient. : "+e.getMessage());
            }
        return httpClient;
    }
}
  1. 在application.properties中将feign.httpclient.enabled设置为true
  2. Spring开机版本为2.4.4。 Feign版本为feign-core-10.10.1

我不明白的其他部分,Spring 如何将这个自定义 CloseableHttpClient 挂钩到它声称的 Feign。因为当我调试时,在运行时我看到 Feign 注释接口由 feign.SynchronousMethodHandler class 实现并且此 class 中的 'client' 字段的类型为 feign.Client 并且在运行时它得到 com.sun.security.ntlm.Client (可能是默认实现)。 CloseableHttpClient 应该如何注入 feign.Client? 网上很少有这样的例子,他们也没有解释。

我在 SOF 上找到了这个 但是

  1. 这里也注入了一个 CloseableHttpClient 类型的@Bean
  2. 对此没有有用的答案。

您需要将 @Configuration 放在 FeignConfig 之上,我相信这应该可以正常工作。

在我们注入的 bean 中,我们必须提供 feign.Client 的实现。最简单的是new Client.Default(SSLSocketFactory, HostnameVerifier)。我在我的问题中更改了代码中的 httpClient @Bean 注入:

@Bean
    public CloseableHttpClient client() {
 CloseableHttpClient httpClient=null;
         try {
                    //... Skipping code for brevity.  
                    //here creating "sslSocketFactory" used in the HttpClient builder below
                    httpClient = HttpClients.custom().setSSLSocketFactory(sslSocketFactory)
                    .setMaxConnTotal(10)
                    .setMaxConnPerRoute(10)
                    .build();
                
            }catch(IOException | KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | CertificateException e) {
                System.err.println("Exception during creation of HttpClient. : "+e.getMessage());
            }
        return httpClient;
    }
              

至:

@Bean
    public feign.Client client() {
      
       feign.Client client=null;
        try {

      //... Skipping code for brevity.  
                    //here creating "sslSocketFactory" used in the HttpClient builder below
      client = new Client.Default(sslSocketFactory, new DefaultHostnameVerifier());
            
        }catch(IOException | KeyManagementException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | CertificateException e) {
            System.err.println("Exception during creation of HttpClient. : "+e.getMessage());
        }
}

经验教训:

documentation 说“您可以通过在使用 Apache 时提供 ClosableHttpClient bean 或在使用 OK HTTP 时提供 OkHttpClient bean 来自定义使用的 HTTP 客户端。”

然后人们说他们在 question which is not answered properly. The bean injection in 中提供的 CloseableHttpClient 将永远无法工作。

最重要的是,OpenFeign 的 Github 文档讨论了如何使用 ApacheHttpClient。

这可能会使人感到困惑。我问题的第二部分 “我不明白的其他部分,Spring 如何将这个自定义的 CloseableHttpClient 挂钩到它声称的 Feign ...... 此 class 中的 'client' 字段的类型为 feign.Client "

答案:

There is no magic, what the ApacheHttpClient OpenFeign Github documentation talks about is OpenFeign's wrapper on the Apache's HttpClient library ApacheHttpClient it implements feign.Client interface.

并且此 ApacheHttpClient 实现在 feign core 10.1.1 依赖项中不可用 Spring openfeign 的引导启动程序。

我也遇到过同样的问题(尽管使用的是 OkHttpClient 而不是 ApacheHttpClient)。你的问题和答案帮助了我。谢谢!

对我来说关键是在自动配置 class (FeignAutoConfiguration) 中我们发现以下内容:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
protected static class OkHttpFeignConfiguration {

...最感兴趣的是这一行:

@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)

...当我们提供自己的 http 客户端 bean(如文档中所述)时,这实际上使 Spring 忽略该配置。

有趣的是 OkHttpFeignConfiguration 本身负责在它自己的包装器 class 中包装 http 客户端(如您在自己的回答中所述):

@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(okhttp3.OkHttpClient client) {
    return new OkHttpClient(client);
}

因此,当我们提供自己的 http 客户端 bean 时,我们实际上禁用了包装器的自动实例化。由于 Feign 在应用程序上下文中找不到其 Client 类型的 bean,因此它自己创建了一个; Default.Client。因此,我们的 http 客户端 bean 被有效地忽略了...

我的解决方案是在配置中自己进行“包装”:

@Bean
fun feignClient(client: OkHttpClient) = feign.okhttp.OkHttpClient(client)

当此 bean 出现在应用程序上下文中时,Feign 将使用它而不是创建其默认客户端。

逻辑与 ApacheHttpClient 案例完全相同。

以上解决方案已经过测试,适用于 Feign 全局配置和客户端特定配置。

据我所知,文档没有说明我们必须自己配置这个“包装器 bean”。我可能在某处遗漏了一些重要的东西,或者在最新版本的库中可能对此行为进行了更改。