淺談SpringCloud feign的http請求組件優(yōu)化方案
1 描述
如果我們直接使用SpringCloud Feign進行服務(wù)間調(diào)用的時候,http組件使用的是JDK的HttpURLConnection,每次請求都會新建一個連接,沒有使用線程池復(fù)用。具體的可以從源碼進行分析
2 源碼分析
我們在分析源碼很難找到入口,不知道從何開始入手,我們在分析SpringCloud feign的時候可用在配置文件下面我講一下個人的思路。
1 首先我點擊@EnableFeignClients 看一下這個注解在哪個資源路徑下
如下圖所示:
2 找到服務(wù)啟動加載的配置文件
3 因為feign底層的負載均衡是基于Ribbon的所以很快就找到了FeignRibbonClientAutoConfiguration.java 這個類
@ConditionalOnClass({ ILoadBalancer.class, Feign.class }) @Configuration @AutoConfigureBefore(FeignAutoConfiguration.class) @EnableConfigurationProperties({ FeignHttpClientProperties.class }) //Order is important here, last should be the default, first should be optional // see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653 @Import({ HttpClientFeignLoadBalancedConfiguration.class, OkHttpFeignLoadBalancedConfiguration.class, DefaultFeignLoadBalancedConfiguration.class }) public class FeignRibbonClientAutoConfiguration {
首先我們從這三個類進行分析,從名字上來看我為了驗證沒有特殊配置,feign底層走的是不是默認的DefaultFeignLoadBalancedConfiguration.class
OkHttpFeignLoadBalancedConfiguration.class
HttpClientFeignLoadBalancedConfiguration.class
DefaultFeignLoadBalancedConfiguration.class
DefaultFeignLoadBalancedConfiguration.class
@Configuration class DefaultFeignLoadBalancedConfiguration { @Bean @ConditionalOnMissingBean public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) { return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory, clientFactory); } }
從上面代碼可知每次請求過來都會創(chuàng)建一個新的client,具體的源碼演示有興趣的可以深入研究,在這里不是我們所研究的重點。
OkHttpFeignLoadBalancedConfiguration.class
@Configuration @ConditionalOnClass(OkHttpClient.class) @ConditionalOnProperty(value = "feign.okhttp.enabled") class OkHttpFeignLoadBalancedConfiguration { @Configuration @ConditionalOnMissingBean(okhttp3.OkHttpClient.class) protected static class OkHttpFeignConfiguration { private okhttp3.OkHttpClient okHttpClient; @Bean @ConditionalOnMissingBean(ConnectionPool.class) public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties, OkHttpClientConnectionPoolFactory connectionPoolFactory) { Integer maxTotalConnections = httpClientProperties.getMaxConnections(); Long timeToLive = httpClientProperties.getTimeToLive(); TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit(); return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit); } @Bean public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) { Boolean followRedirects = httpClientProperties.isFollowRedirects(); Integer connectTimeout = httpClientProperties.getConnectionTimeout(); this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()). connectTimeout(connectTimeout, TimeUnit.MILLISECONDS). followRedirects(followRedirects). connectionPool(connectionPool).build(); return this.okHttpClient; } @PreDestroy public void destroy() { if(okHttpClient != null) { okHttpClient.dispatcher().executorService().shutdown(); okHttpClient.connectionPool().evictAll(); } } } @Bean @ConditionalOnMissingBean(Client.class) public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) { OkHttpClient delegate = new OkHttpClient(okHttpClient); return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory); } }
從源碼可以看出
1 該類是個配置類,當引入OkHttpClient.Class會加載
client方法中可以看出會返回一個http連接池的client HttpClientFeignLoadBalancedConfiguration @Configuration @ConditionalOnClass(ApacheHttpClient.class) @ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true) class HttpClientFeignLoadBalancedConfiguration {
這個類和OkHttpFeignLoadBalancedConfiguration原理類型
使用OKHttp替代默認的JDK的HttpURLConnection
使用appach httpclient使用教程類似
使用方法
1 pom
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency>
2 Yml文件
feign: okhttp: enabled: true
3 自定義連接池
可以通過代碼進行配置,也可以通過yml配置
@Configuration @ConditionalOnClass(Feign.class) @AutoConfigureBefore(FeignAutoConfiguration.class) public class FeignOkHttpConfig { @Bean public okhttp3.OkHttpClient okHttpClient(){ return new okhttp3.OkHttpClient.Builder() .readTimeout(60,TimeUnit.SECONDS) .connectTimeout(60,TimeUnit.SECONDS) .connectionPool(new ConnectionPool()) .build(); } }
驗證
默認的Feign處理會走到如下位置;
位置處于如下圖所示
@Override public Response execute(Request request, Options options) throws IOException { HttpURLConnection connection = convertAndSend(request, options); return convertResponse(connection).toBuilder().request(request).build(); }
走okhttp客戶端會走如下代碼
具體位置如下圖所示:
@Override public Response execute() throws IOException { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); try { client.dispatcher().executed(this); Response result = getResponseWithInterceptorChain(); if (result == null) throw new IOException("Canceled"); return result; } finally { client.dispatcher().finished(this); } }
驗證結(jié)果
如下所示:
彩蛋
okhttp客戶端會走的代碼可以看出來okhttp有synchronized鎖線程安全的那默認的是否是線程安全的呢 有待去驗證。
追加
如果發(fā)現(xiàn)配置的超時時間無效,可以添加以下配置,因為讀取超時配置的時候沒有讀取上面的okhttp的配置參數(shù),而是從Request中讀取。
具體配置如下所示:
@Bean public Request.Options options(){ return new Request.Options(60000,60000); }
補充:springCloud feign使用/優(yōu)化總結(jié)
基于springCloud Dalston.SR3版本
1.當接口參數(shù)是多個的時候 需要指定@RequestParam 中的value來明確一下。
/** * 用戶互掃 * @param uid 被掃人ID * @param userId 當前用戶ID * @return */ @PostMapping(REQ_URL_PRE + "/qrCodeReturnUser") UserQrCode qrCodeReturnUser(@RequestParam("uid") String uid,@RequestParam("userId") Integer userId);
2.接口參數(shù)為對象的時候 需要使用@RequestBody注解 并采用POST方式。
3.如果接口是簡單的數(shù)組/列表參數(shù) 這里需要使用Get請求才行
@GetMapping(REQ_URL_PRE + "/getUserLevels") Map<Integer, UserLevel> getUserLevels(@RequestParam("userIds") List<Integer> userIds);
4.直接可以在@FeignClient中配置降級處理方式 對于一些不重要的業(yè)務(wù) 自定義處理很有幫助
@FeignClient(value = "cloud-user", fallback = IUsers.UsersFallback.class)
5.feign默認只有HystrixBadRequestException異常不會走熔斷,其它任何異常都會進入熔斷,需要重新實現(xiàn)一下ErrorDecoder包裝業(yè)務(wù)異常
示例:https://github.com/peachyy/feign-support
6. feign HTTP請求方式選擇
feign默認使用的是基于JDK提供的URLConnection調(diào)用HTTP接口,不具備連接池。所以資源開銷上有點影響,經(jīng)測試JDK的URLConnection比Apache HttpClient快很多倍。但是Apache HttpClient和okhttp都支持配置連接池功能。具體選擇需要權(quán)衡
7.默認不啟用hystrix 需要手動指定feign.hystrix.enabled=true 開啟熔斷
8.啟用壓縮也是一種有效的優(yōu)化方式
feign.compression.request.enabled=true feign.compression.response.enabled=true feign.compression.request.mime-types=text/xml,application/xml,application/json
9.參數(shù)相關(guān)調(diào)優(yōu)
hystrix線程數(shù)設(shè)置
設(shè)置參數(shù)hystrix.threadpool.default.coreSize 來指定熔斷隔離的線程數(shù) 這個數(shù)需要調(diào)優(yōu),經(jīng)測試 線程數(shù)我們設(shè)置為和提供方的容器線程差不多,吞吐量高許多。
第一次訪問服務(wù)出錯的問題
啟用Hystrix后,很多服務(wù)當?shù)谝淮卧L問的時候都會失敗 是因為初始化負載均衡一系列操作已經(jīng)超出了超時時間了 默認的超時時間為1S,設(shè)置參數(shù)超時時間hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=30000 可解決這個問題。
負載均衡參數(shù)設(shè)置
設(shè)置了Hystrix的超時參數(shù)會 還需設(shè)置一下ribbon的相關(guān)參數(shù) 這些參數(shù)和Hystrix的超時參數(shù)有一定的邏輯關(guān)系
請求處理的超時時間 ribbon.ReadTimeout=120000
請求連接的超時時間 ribbon.ConnectTimeout=30000
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
SpringBoot FailureAnalyzer實例使用教程
FailureAnalyzer是一種在啟動時攔截exception并將其轉(zhuǎn)換為human-readable消息的好方法,包含在故障分析中。SpringBoot為application context相關(guān)的exceptions,JSR-303驗證等提供了這樣的分析器,實際上很容易創(chuàng)建自己的2022-12-12Java多線程編程之ThreadLocal線程范圍內(nèi)的共享變量
這篇文章主要介紹了Java多線程編程之ThreadLocal線程范圍內(nèi)的共享變量,本文講解了ThreadLocal的作用和目的、ThreadLocal的應(yīng)用場景、ThreadLocal的使用實例等,需要的朋友可以參考下2015-05-05Hibernate中l(wèi)oad方法與get方法的區(qū)別
Hibernate中有兩個極為相似的方法get()與load(),他們都可以通過指定的實體類與ID從數(shù)據(jù)庫中讀取數(shù)據(jù),并返回對應(yīng)的實例,但Hibernate不會搞兩個完全一樣的方法的2016-01-01mybatis的動態(tài)sql之if test的使用說明
這篇文章主要介紹了mybatis的動態(tài)sql之if test的使用說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02MybatisPlus創(chuàng)建時間不想用默認值的問題
MybatisPlus通過FieldFill注解和MpMetaObjectHandler類支持自動填充字段功能,特別地,可以設(shè)置字段在插入或更新時自動填充創(chuàng)建時間和更新時間,但在特定場景下,如導(dǎo)入數(shù)據(jù)時,可能需要自定義創(chuàng)建時間2024-09-09Java處理時間格式CST和GMT轉(zhuǎn)換方法示例
這篇文章主要給大家介紹了關(guān)于Java處理時間格式CST和GMT轉(zhuǎn)換方法的相關(guān)資料,相信很多小伙伴在時間格式轉(zhuǎn)換的時候非常頭疼,文中通過代碼示例介紹的非常詳細,需要的朋友可以參考下2023-09-09淺試仿?mapstruct實現(xiàn)微服務(wù)編排框架詳解
這篇文章主要為大家介紹了淺試仿?mapstruct實現(xiàn)微服務(wù)編排框架詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-08-08使用SpringBoot簡單實現(xiàn)無感知的刷新 Token功能
實現(xiàn)無感知的刷新 Token 是一種提升用戶體驗的常用技術(shù),可以在用戶使用應(yīng)用時自動更新 Token,無需用戶手動干預(yù),這種技術(shù)在需要長時間保持用戶登錄狀態(tài)的應(yīng)用中非常有用,以下是使用Spring Boot實現(xiàn)無感知刷新Token的一個場景案例和相應(yīng)的示例代碼2024-09-09