在默认情况下 spring cloud feign在进行各个子服务之间的调用时,http组件使用的是jdk的HttpURLConnection,没有使用线程池。源码分析feign的http组件对象生成的过程,然后通过为feign配置http线程池优化调用效率。
我们分析源码spring cloud feign。在spring-cloud-netflix-core/META-INF/spring.factories中可以看到,在spring boot自动配置会初始化FeignRibbonClientAutoConfiguration,这个类会生成Ribbon的使用http组件。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration,\\
分析配置类是FeignRibbonClientAutoConfiguration下面分析此类import的3个类:HttpClientFeignLoadBalancedConfiguration,OkHttpFeignLoadBalancedConfiguration,DefaultFeignLoadBalancedConfiguration
<code>@Import({ HttpClientFeignLoadBalancedConfiguration.class, OkHttpFeignLoadBalancedConfiguration.class, DefaultFeignLoadBalancedConfiguration.class })public class FeignRibbonClientAutoConfiguration { …}/<code>
HttpClientFeignLoadBalancedConfiguration为feigin配置appache client的线程池当引入ApacheHttpClient.class类时,会初始化这个配置类方法feignClient()中:根据@ConditionalOnMissingBean(Client.class)知道如果有HttpClient 对象,则创建的ApacheHttpClient使用自己定义的HttpClient 。如果没有,则使用默认值。最后生成LoadBalancerFeignClient对象
<code>@Configuration@ConditionalOnClass(ApacheHttpClient.class)@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)class HttpClientFeignLoadBalancedConfiguration { @Autowired(required = false) private HttpClient httpClient; @Bean @ConditionalOnMissingBean(Client.class) public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) { ApacheHttpClient delegate; if (this.httpClient != null) { delegate = new ApacheHttpClient(this.httpClient); } else { delegate = new ApacheHttpClient(); } return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory); }}/<code>
OkHttpFeignLoadBalancedConfiguration为feigin配置OkHttp,类似apache httpclient, 这里略。DefaultFeignLoadBalancedConfiguration为feigin配置HttpURLConnection,方法feignClient():只有以上两个Client没有生产对象时,才在这个方法中使用Client.Default生成LoadBalancerFeignClient
<code>@Configurationclass DefaultFeignLoadBalancedConfiguration { @Bean @ConditionalOnMissingBean public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) { return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory, clientFactory); }}/<code>
查看Client.Default的源码,Default 使用HttpURLConnection 建立连接且每次请求都建立一个新的连接
<code> public static class Default implements Client { @Override public Response execute(Request request, Options options) throws IOException { HttpURLConnection connection = convertAndSend(request, options); return convertResponse(connection).toBuilder().request(request).build(); } ….}/<code>
综上所述,在默认情况下,spring cloud 没有引入httpclient和okhttp的jar包,所有默认使用HttpURLConnection
1. 替换OKHttp
由此可见feigin优化需要引入线程池有2种可选的线程池:HttpClient和OKHttp比较推荐OKHttp,请求封装的非常简单易用,性能也很ok。
1.1. 添加依赖
<code><dependency> <groupid>com.squareup.okhttp3/<groupid> <artifactid>okhttp/<artifactid>/<dependency>/<code>
1.2. 修改配置文件
<code>feign: okhttp: enabled: true httpclient: enabled: false max-connections: 1000 max-connections-per-route: 100/<code>
max-connections:最大连接数max-connections-per-route:每个url的连接数
2. 开启Feign请求响应压缩
开启压缩可以有效节约网络资源,但是会增加CPU压力,建议把最小压缩的文档大小适度调大一点
<code>## 开启Feign请求响应压缩feign.compression.request.enabled=truefeign.compression.response.enabled=true## 配置压缩文档类型及最小压缩的文档大小feign.compression.request.mime-types=text/xml,application/xml,application/jsonfeign.compression.request.min-request-size=2048/<code>
三、Ribbon参数调优
主要调整请求的超时时间,是否重试
假如业务没有做幂等性的话建议把重试关掉ribbon.MaxAutoRetriesNextServer=0
<code>## 从注册中心刷新servelist的时间 默认30秒,单位msribbon.ServerListRefreshInterval=15000## 请求连接的超时时间 默认1秒,单位msribbon.ConnectTimeout=30000## 请求处理的超时时间 默认1秒,单位msribbon.ReadTimeout=30000## 对所有操作请求都进行重试,不配置这个MaxAutoRetries不起作用 默认false#ribbon.OkToRetryOnAllOperations=true## 对当前实例的重试次数 默认0#ribbon.MaxAutoRetries=1## 切换实例的重试次数 默认1ribbon.MaxAutoRetriesNextServer=0/<code>
如果MaxAutoRetries=1和MaxAutoRetriesNextServer=1请求在1s内响应,超过1秒先同一个服务器上重试1次,如果还是超时或失败,向其他服务上请求重试1次。那么整个ribbon请求过程的超时时间为:ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1)
ribbon fegin第一次请求慢问题
开启ribbon饥饿模式
<code>ribbon: eager-load: enabled: true /<code>
feigin 日志输出
<code>package com.open.capacity.server.config;import org.springframework.context.annotation.Bean;import feign.Logger.Level;public class GolbalFeignConfig {@Beanpublic Level levl(){return Level.FULL;}}/<code>
<code>@EnableLogging@EnableDiscoveryClient@SpringBootApplication@EnableFeignClients(defaultConfiguration=GolbalFeignConfig.class)public class AuthServerApp {public static void main(String[] args) {//固定端口启动//SpringApplication.run(OpenAuthServerApp.class, args);//随机端口启动SpringApplication app = new SpringApplication(AuthServerApp.class); app.addListeners(new PortApplicationEnvironmentPreparedEventListener()); app.run(args);}}/<code>
<code>##feign参数优化feign: client: config: default: loggerLevel: full ## 配合logging.level=trace debug用于开发调式日志logging: level: com.open.capacity: TRACE org.hibernate: INFO org.hibernate.type.descriptor.sql.BasicBinder: TRACE org.hibernate.type.descriptor.sql.BasicExtractor: TRACE/<code>
閱讀更多 weal1992 的文章