open feign client 切换httpclient
在工作中碰到如下异常:
java.net.ProtocolException: Invalid HTTP method: PATCH
在度娘的帮助下知道,openfeign底层默认用HttpURLConnection发起请求。而HttpURLConnection并不支持PATCH。需要将底层请求替换为httpclient。
经过查询如下方案(并未成功)。
1.pom文件中引入依赖:
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> <version>9.4.0</version> </dependency>
2.配置文件中添加配置:
feign.httpclient.enabled=true
完成上面两步即可替换为httpclient。
可参考: 怎样配置Feign使用HttpClientcom/p/d063c40df8d6?utm_campaign
因为我需要给client配置ssl,没有使用第三步。但是呢,我并没有成功。
因此去查看openfeiopgn源码:发现如下类:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnBean(BlockingLoadBalancerClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
@Import(HttpClientFeignConfiguration.class)
class HttpClientFeignLoadBalancerConfiguration {
@Bean
@ConditionalOnMissingBean
public Client feignClient(BlockingLoadBalancerClient loadBalancerClient,
HttpClient httpClient) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient);
}
我们看到要想使用feign.httpclient.enabled=true的先决条件有两个,
1.ApacheHttpClient类
2.BlockingLoadBalancerClient实例
其中1,我们通常过引入httpclient包已解决,2呢我们没有引入这个类所以没法初始化。(主要是太懒不想再引入jar包)
至此找到了我们按照之前方案不成功的原因。
此时通过报错信息发现方法执行位置在SynchronousMethodHandler类中。
Caused by: java.net.ProtocolException: Invalid HTTP method: PATCH
at java.base/java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:487)
at java.base/sun.net.www.protocol.http.HttpURLConnection.setRequestMethod(HttpURLConnection.java:571)
at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.setRequestMethod(HttpsURLConnectionImpl.java:344)
at feign.Client$Default.convertAndSend(Client.java:133)
at feign.Client$Default.execute(Client.java:73)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:110)
... 31 common frames omitted
发现此类中有client属性,有了点头绪,这个属性是在哪里被注入的呢?我们跟踪代码发现client传入链条如下:
SynchronousMethodHandler 构造方法->内部类Factory构造方法->Feign的内部类Builder中构建。
SynchronousMethodHandler内部类Factory:
static class Factory {
private final Client client;
private final Retryer retryer;
private final List<RequestInterceptor> requestInterceptors;
private final Logger logger;
private final Logger.Level logLevel;
private final boolean decode404;
private final boolean closeAfterDecode;
private final ExceptionPropagationPolicy propagationPolicy;
Factory(Client client, Retryer retryer, List<RequestInterceptor> requestInterceptors,
Logger logger, Logger.Level logLevel, boolean decode404, boolean closeAfterDecode,
ExceptionPropagationPolicy propagationPolicy) {
this.client = checkNotNull(client, "client");
this.retryer = checkNotNull(retryer, "retryer");
this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors");
this.logger = checkNotNull(logger, "logger");
this.logLevel = checkNotNull(logLevel, "logLevel");
this.decode404 = decode404;
this.closeAfterDecode = closeAfterDecode;
this.propagationPolicy = propagationPolicy;
public MethodHandler create(Target<?> target,
MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
Options options,
Decoder decoder,
ErrorDecoder errorDecoder) {
return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
logLevel, md, buildTemplateFromArgs, options, decoder,
errorDecoder, decode404, closeAfterDecode, propagationPolicy);
}
Feign内部类Builder:
。。。
private Client client = new Client.Default(null, null);
public Builder client(Client client) {
this.client = client;
return this;
public Feign build() {
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
这时我们发现client被赋值为默认的Feign client。但是它提供了修改client的方法。
那这个类是怎么被加载的?我们知道springboot的bean都会自动装配,去openfeign源码中查看相关自动配置类,发现如下类中FeignClientsConfiguration提供了默认加载方法:
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
return Feign.builder().retryer(retryer);
只要自定义这个bean将httpclient注入,那么理论上调用的时候就会使用httpclient。在自己的config类中添加如下bean。
@Bean
public Feign.Builder feignBuilder(ApacheHttpClient httpClient) {
return Feign.builder().client(httpClient);
@Bean
public ApacheHttpClient apacheHttpClient() {
CloseableHttpClient closeableHttpClient = HttpClientBuilder