服务器之家

服务器之家 > 正文

浅谈SpringCloud feign的http请求组件优化方案

时间:2021-08-13 13:49     来源/作者:小鱼儿_karl

1 描述

如果我们直接使用SpringCloud Feign进行服务间调用的时候,http组件使用的是JDK的HttpURLConnection,每次请求都会新建一个连接,没有使用线程池复用。具体的可以从源码进行分析

2 源码分析

我们在分析源码很难找到入口,不知道从何开始入手,我们在分析SpringCloud feign的时候可用在配置文件下面我讲一下个人的思路。

1 首先我点击@EnableFeignClients 看一下这个注解在哪个资源路径下

如下图所示:

浅谈SpringCloud feign的http请求组件优化方案

2 找到服务启动加载的配置文件

浅谈SpringCloud feign的http请求组件优化方案

3 因为feign底层的负载均衡是基于Ribbon的所以很快就找到了FeignRibbonClientAutoConfiguration.java 这个类

  1. @ConditionalOnClass({ ILoadBalancer.class, Feign.class })
  2. @Configuration
  3. @AutoConfigureBefore(FeignAutoConfiguration.class)
  4. @EnableConfigurationProperties({ FeignHttpClientProperties.class })
  5. //Order is important here, last should be the default, first should be optional
  6. // see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
  7. @Import({ HttpClientFeignLoadBalancedConfiguration.class,
  8. OkHttpFeignLoadBalancedConfiguration.class,
  9. DefaultFeignLoadBalancedConfiguration.class })
  10. public class FeignRibbonClientAutoConfiguration {

首先我们从这三个类进行分析,从名字上来看我为了验证没有特殊配置,feign底层走的是不是默认的DefaultFeignLoadBalancedConfiguration.class

OkHttpFeignLoadBalancedConfiguration.class

HttpClientFeignLoadBalancedConfiguration.class

DefaultFeignLoadBalancedConfiguration.class

DefaultFeignLoadBalancedConfiguration.class

  1. @Configuration
  2. class DefaultFeignLoadBalancedConfiguration {
  3. @Bean
  4. @ConditionalOnMissingBean
  5. public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
  6. SpringClientFactory clientFactory) {
  7. return new LoadBalancerFeignClient(new Client.Default(null, null),
  8. cachingFactory, clientFactory);
  9. }
  10. }

从上面代码可知每次请求过来都会创建一个新的client,具体的源码演示有兴趣的可以深入研究,在这里不是我们所研究的重点。

OkHttpFeignLoadBalancedConfiguration.class

  1. @Configuration
  2. @ConditionalOnClass(OkHttpClient.class)
  3. @ConditionalOnProperty(value = "feign.okhttp.enabled")
  4. class OkHttpFeignLoadBalancedConfiguration {
  5. @Configuration
  6. @ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
  7. protected static class OkHttpFeignConfiguration {
  8. private okhttp3.OkHttpClient okHttpClient;
  9. @Bean
  10. @ConditionalOnMissingBean(ConnectionPool.class)
  11. public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
  12. OkHttpClientConnectionPoolFactory connectionPoolFactory) {
  13. Integer maxTotalConnections = httpClientProperties.getMaxConnections();
  14. Long timeToLive = httpClientProperties.getTimeToLive();
  15. TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
  16. return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
  17. }
  18. @Bean
  19. public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
  20. ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
  21. Boolean followRedirects = httpClientProperties.isFollowRedirects();
  22. Integer connectTimeout = httpClientProperties.getConnectionTimeout();
  23. this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()).
  24. connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
  25. followRedirects(followRedirects).
  26. connectionPool(connectionPool).build();
  27. return this.okHttpClient;
  28. }
  29. @PreDestroy
  30. public void destroy() {
  31. if(okHttpClient != null) {
  32. okHttpClient.dispatcher().executorService().shutdown();
  33. okHttpClient.connectionPool().evictAll();
  34. }
  35. }
  36. }
  37. @Bean
  38. @ConditionalOnMissingBean(Client.class)
  39. public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
  40. SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
  41. OkHttpClient delegate = new OkHttpClient(okHttpClient);
  42. return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
  43. }
  44. }

从源码可以看出

1 该类是个配置类,当引入OkHttpClient.Class会加载

  1. client方法中可以看出会返回一个http连接池的client
  2. HttpClientFeignLoadBalancedConfiguration
  3. @Configuration
  4. @ConditionalOnClass(ApacheHttpClient.class)
  5. @ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
  6. class HttpClientFeignLoadBalancedConfiguration {

这个类和OkHttpFeignLoadBalancedConfiguration原理类型

使用OKHttp替代默认的JDK的HttpURLConnection

使用appach httpclient使用教程类似

使用方法

1 pom

  1. <dependency>
  2. <groupId>io.github.openfeign</groupId>
  3. <artifactId>feign-okhttp</artifactId>
  4. </dependency>

2 Yml文件

  1. feign:
  2. okhttp:
  3. enabled: true

3 自定义连接池

可以通过代码进行配置,也可以通过yml配置

  1. @Configuration
  2. @ConditionalOnClass(Feign.class)
  3. @AutoConfigureBefore(FeignAutoConfiguration.class)
  4. public class FeignOkHttpConfig {
  5. @Bean
  6. public okhttp3.OkHttpClient okHttpClient(){
  7. return new okhttp3.OkHttpClient.Builder()
  8. .readTimeout(60,TimeUnit.SECONDS)
  9. .connectTimeout(60,TimeUnit.SECONDS)
  10. .connectionPool(new ConnectionPool())
  11. .build();
  12. }
  13. }

验证

默认的Feign处理会走到如下位置;

位置处于如下图所示

浅谈SpringCloud feign的http请求组件优化方案

  1. @Override
  2. public Response execute(Request request, Options options) throws IOException {
  3. HttpURLConnection connection = convertAndSend(request, options);
  4. return convertResponse(connection).toBuilder().request(request).build();
  5. }

走okhttp客户端会走如下代码

具体位置如下图所示:

浅谈SpringCloud feign的http请求组件优化方案

  1. @Override public Response execute() throws IOException {
  2. synchronized (this) {
  3. if (executed) throw new IllegalStateException("Already Executed");
  4. executed = true;
  5. }
  6. captureCallStackTrace();
  7. try {
  8. client.dispatcher().executed(this);
  9. Response result = getResponseWithInterceptorChain();
  10. if (result == null) throw new IOException("Canceled");
  11. return result;
  12. } finally {
  13. client.dispatcher().finished(this);
  14. }
  15. }

验证结果

如下所示:

浅谈SpringCloud feign的http请求组件优化方案

彩蛋

okhttp客户端会走的代码可以看出来okhttp有synchronized锁线程安全的那默认的是否是线程安全的呢 有待去验证。

追加

如果发现配置的超时时间无效,可以添加以下配置,因为读取超时配置的时候没有读取上面的okhttp的配置参数,而是从Request中读取。

具体配置如下所示:

  1. @Bean
  2. public Request.Options options(){
  3. return new Request.Options(60000,60000);
  4. }

补充:springCloud feign使用/优化总结

基于springCloud Dalston.SR3版本

1.当接口参数是多个的时候 需要指定@RequestParam 中的value来明确一下。

  1. /**
  2. * 用户互扫
  3. * @param uid 被扫人ID
  4. * @param userId 当前用户ID
  5. * @return
  6. */
  7. @PostMapping(REQ_URL_PRE + "/qrCodeReturnUser")
  8. UserQrCode qrCodeReturnUser(@RequestParam("uid") String uid,@RequestParam("userId") Integer userId);

2.接口参数为对象的时候 需要使用@RequestBody注解 并采用POST方式。

3.如果接口是简单的数组/列表参数 这里需要使用Get请求才行

  1. @GetMapping(REQ_URL_PRE + "/getUserLevels")
  2. Map<Integer, UserLevel> getUserLevels(@RequestParam("userIds") List<Integer> userIds);

4.直接可以在@FeignClient中配置降级处理方式 对于一些不重要的业务 自定义处理很有帮助

  1. @FeignClient(value = "cloud-user", fallback = IUsers.UsersFallback.class)

5.feign默认只有HystrixBadRequestException异常不会走熔断,其它任何异常都会进入熔断,需要重新实现一下ErrorDecoder包装业务异常

示例:https://github.com/peachyy/feign-support

6. feign HTTP请求方式选择

feign默认使用的是基于JDK提供的URLConnection调用HTTP接口,不具备连接池。所以资源开销上有点影响,经测试JDK的URLConnection比Apache HttpClient快很多倍。但是Apache HttpClient和okhttp都支持配置连接池功能。具体选择需要权衡

7.默认不启用hystrix 需要手动指定feign.hystrix.enabled=true 开启熔断

8.启用压缩也是一种有效的优化方式

  1. feign.compression.request.enabled=true
  2. feign.compression.response.enabled=true
  3. feign.compression.request.mime-types=text/xml,application/xml,application/json

9.参数相关调优

hystrix线程数设置

设置参数hystrix.threadpool.default.coreSize 来指定熔断隔离的线程数 这个数需要调优,经测试 线程数我们设置为和提供方的容器线程差不多,吞吐量高许多。

第一次访问服务出错的问题

启用Hystrix后,很多服务当第一次访问的时候都会失败 是因为初始化负载均衡一系列操作已经超出了超时时间了 默认的超时时间为1S,设置参数超时时间hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=30000 可解决这个问题。

负载均衡参数设置

设置了Hystrix的超时参数会 还需设置一下ribbon的相关参数 这些参数和Hystrix的超时参数有一定的逻辑关系

请求处理的超时时间 ribbon.ReadTimeout=120000

请求连接的超时时间 ribbon.ConnectTimeout=30000

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

原文链接:https://blog.csdn.net/qq_33249725/article/details/88532160

相关文章

热门资讯

yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总 2020-11-13
2021德云社封箱演出完整版 2021年德云社封箱演出在线看
2021德云社封箱演出完整版 2021年德云社封箱演出在线看 2021-03-15
返回顶部