众所周知,我们在使用SpringCloud OpenFeign时,默认使用的是老旧的连接器HttpURLConnection
。性能以及并发量方面都差强人意。
一般而言都会对其进行优化调整。本文采用OpenFeign整合okHttp的方式替换原有的Client,去做请求。
使用java 17,spring cloud 4.0.4,springboot 3.1.4
使用项目是本系列第一篇中的项目
本文介绍两种方式的配置,一个是LoadBalancer 的,都是默认带有连接池的。
OkHttpFeignConfiguration
OkHttpFeignLoadBalancerConfiguration
首先我们需要加入什么依赖配置呢?
依据 OkHttpFeignConfiguration
的配置内容,进行分析:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty("spring.cloud.openfeign.okhttp.enabled")
protected static class OkHttpFeignConfiguration {
// 类路径下有连接池时,构建连接池
@Bean
@ConditionalOnMissingBean(ConnectionPool.class)
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties) {
int maxTotalConnections = httpClientProperties.getMaxConnections();
long timeToLive = httpClientProperties.getTimeToLive();
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
return new ConnectionPool(maxTotalConnections, timeToLive, ttlUnit);
}
// 构建OkHttpClient实例
@Bean
public okhttp3.OkHttpClient okHttpClient(okhttp3.OkHttpClient.Builder builder, ConnectionPool connectionPool,
FeignHttpClientProperties httpClientProperties) {
boolean followRedirects = httpClientProperties.isFollowRedirects();
int connectTimeout = httpClientProperties.getConnectionTimeout();
boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
Duration readTimeout = httpClientProperties.getOkHttp().getReadTimeout();
List<Protocol> protocols = httpClientProperties.getOkHttp().getProtocols().stream().map(Protocol::valueOf)
.collect(Collectors.toList());
if (disableSslValidation) {
disableSsl(builder);
}
this.okHttpClient = builder.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.followRedirects(followRedirects).readTimeout(readTimeout).connectionPool(connectionPool)
.protocols(protocols).build();
return this.okHttpClient;
}
}
从配置类中观察到,它的启用条件有2个,一个是需要引入 okhttp的包,另一个需要一行启用配置:
<dependency>
<groupId>io.github.openfeigngroupId>
<artifactId>feign-okhttpartifactId>
<version>13.1version>
dependency>
# 启用okhttp配置
spring.cloud.openfeign.okhttp.enabled=true
正常请求,在SynchronousMethodHandler.executeAndDecode(...)
中打断点,调试观察client的类型:
发现已经不再是之前的Default
了,换成了OkHttpClient
类型。并且,也内置了连接池以及一些基本配置信息。
依据 OkHttpFeignLoadBalancerConfiguration
中的配置项,分析:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty("spring.cloud.openfeign.okhttp.enabled")
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
@EnableConfigurationProperties(LoadBalancerClientsProperties.class)
class OkHttpFeignLoadBalancerConfiguration {
@Bean
@ConditionalOnMissingBean
@Conditional(OnRetryNotEnabledCondition.class)
public Client feignClient(okhttp3.OkHttpClient okHttpClient, LoadBalancerClient loadBalancerClient,
LoadBalancerClientFactory loadBalancerClientFactory,
List<LoadBalancerFeignRequestTransformer> transformers) {
OkHttpClient delegate = new OkHttpClient(okHttpClient);
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancerClientFactory,
transformers);
}
// 省略其他配置
}
想要让这个配置生效,需要满足OkHttpClient
、LoadBalancerClient
、OnRetryNotEnabledCondition
。
也就是2个依赖项,2个配置:
<dependency>
<groupId>io.github.openfeigngroupId>
<artifactId>feign-okhttpartifactId>
<version>13.1version>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-loadbalancerartifactId>
<version>4.0.4version>
dependency>
而配置方面需要增加:
# 启用okhttp配置
spring.cloud.openfeign.okhttp.enabled=true
# 关闭负载重试
spring.cloud.loadbalancer.retry.enabled=false
正常请求,在SynchronousMethodHandler.executeAndDecode(...)
中打断点,调试观察client的类型:
发现已经不再是之前的Default
了,换成了OkHttpClient
类型。
SpringCloud系列文章目录(总纲篇)
增加以下拦截器配置,对请求和响应进行日志打印。
package org.feng.config;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
/**
* okhttp配置
*
* @author feng
*/
@Slf4j
@Configuration
public class OkHttpConfig {
@Bean
public okhttp3.OkHttpClient.Builder okHttpClientBuilder() {
return new okhttp3.OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor());
}
/**
* okhttp3 请求日志拦截器
*/
static class LoggingInterceptor implements Interceptor {
@NotNull
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long start = System.nanoTime();
log.info(String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long end = System.nanoTime();
log.info(String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (end - start) / 1e6d, response.headers()));
return response;
}
}
}
可以观察到日志中,打印出来了请求路径、请求头等信息。
2023-11-24T16:48:56.358+08:00 INFO 35987 --- [nio-8080-exec-1] org.feng.config.OkHttpConfig : Sending request http://localhost:10080/hello/post on null
Content-Length: 100
Accept: */*
2023-11-24T16:48:56.713+08:00 INFO 35987 --- [nio-8080-exec-1] org.feng.config.OkHttpConfig : Received response for http://localhost:10080/hello/post in 347.4ms
Content-Type: application/json
Transfer-Encoding: chunked
Date: Fri, 24 Nov 2023 08:48:56 GMT
Keep-Alive: timeout=60
Connection: keep-alive