先思考幾個問題:
- 什么是百萬並發連接?
- 什么是吞吐量?
- 操作系統能否支持百萬連接?
- 操作系統維持百萬連接需要多少內存?
- 應用程序維持百萬連接需要多少內存?
- 百萬連接的吞吐量是否超過了網絡限制?
百萬的並發連接挑戰意味着什么:
- 100 萬的並發連接數
- 10 萬個連接/秒——(如果每個連接以這個速率持續約10秒)
- 1 GB/秒的連接——快速連接到互聯網。
- 100 萬個數據包/秒——據估計目前的服務器每秒處理50K的數據包,以后會更多。過去服務器每秒可以處理100K的中斷,並且每一個數據包都產生中斷。
- 10 微秒的延遲——可擴展服務器也許可以處理這個規模,但延遲可能會飆升。
- 10 微秒的抖動——限制最大延遲
- 並發10核技術——軟件應支持更多核的服務器。通常情況下,軟件能輕松擴展到四核。服務器可以擴展到更多核,因此需要重寫軟件,以支持更多核的服務器。
一、操作系統參數優化
在 Linux 下,我們可以通過 ulimit -a 命令查看典型的機器默認的限制情況:
其中的 open files 是指一個進程能同時打開的文件句柄數量,默認值為1024
,對於一些需要大量文件句柄的程序,如web服務器、數據庫程序等,1024
往往是不夠用的,在句柄使用完畢的時候,系統就會頻繁出現"too many open files"
錯誤。
在Linux平台上,無論是編寫客戶端程序還是服務端程序,在進行高並發TCP連接處理時,由於每個TCP連接都要創建一個socket句柄,而每個socket句柄同時也是一個文件句柄,所以其最高並發數量要受到系統對用戶單一進程同時可打開文件數量的限制以及整個系統可同時打開的文件數量限制。
1:解決文件句柄數量受限
1.1:單一進程的文件句柄數量受限
我們可以ulimit命令查看當前用戶進程可打開的文件句柄數限制:
[root@localhost ~]# ulimit -n 1024
這表示當前用戶的每個進程最多允許同時打開1024
個文件,除去每個進程必然打開的標准輸入、標准輸出、標准錯誤、服務器監聽socket、進程間通訊的unix域socket等文件,剩下的可用於客戶端socket連接的文件數就只有大概1024-10=1014個左右。也就是說,在默認情況下,基於Linux的通訊程序最多允許同時1014
個TCP並發連接。
對於想支持更高數量的TCP並發連接的通訊處理程序,就必須修改Linux對當前用戶的進程可同時打開的文件數量的軟限制(soft limit)
和硬限制(hardlimit)
。其中:
軟限制
是指Linux在當前系統能夠承受的范圍內進一步限制用戶能同時打開的文件數。硬限制
是指根據系統硬件資源狀況(主要是系統內存)計算出來的系統最多可同時打開的文件數量。
通常軟限制小於或等於硬限制,可通過ulimit命令查看軟限制和硬限制:
[root@localhost ~]# ulimit -Sn 1024 [root@localhost ~]# ulimit -Hn 4096
修改單一進程能同時打開的文件句柄數有2種方法:
1、直接使用ulimit命令,如:
[root@localhost ~]# ulimit -n 1048576
執行成功之后,ulimit n、Sn、Hn的值均會變為1048576。但該方法設置的值只會在當前終端有效,且設置的值不能高於方法2中設置的值。
2、對 /etc/security/limits.conf 文件,添加或修改:
* soft nofile 1048576 * hard nofile 1048576
* hard nofile 1048576
其中,
*
代表對所有用戶有效,若僅想針對某個用戶,可替換星號。soft
即軟限制,它只是一個警告值。hard
代表硬限制,是一個真正意義的閾值,超過就會報錯。nofile
表示打開文件的最大數量。
注:1048576 = 1024 1024,為什么要取這個值呢? 因為在linux kernel 2.6.25之前,通過ulimit -n(setrlimit(RLIMIT_NOFILE))設置每個進程的最大打開文件句柄數不能超過NR_OPEN(10241024),也就是100多w(除非重新編譯內核),而在25之后,內核導出了一個sys接口可以修改這個最大值(/proc/sys/fs /nr_open). 具體的changelog在 https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=9cfe015aa424b3c003baba3841a60dd9b5ad319b
注意文件保存之后,需要注銷或重啟系統方能生效。
1.2:整個系統的文件句柄數量受限
解決完單一進程的文件句柄數量受限問題后,還要解決整個系統的文件句柄數量受限問題。我們可通過以下命令查看Linux系統級的最大打開文件數限制:
[root@iZ2zehwfmp0tro35kljx2kZ ~]# cat /proc/sys/fs/file-max 764408 [root@iZ2zehwfmp0tro35kljx2kZ ~]# cat /proc/sys/fs/file-nr 1440 0 764408
file-max
表示系統所有進程最多允許同時打開的文件句柄數,是Linux系統級硬限制。通常,這個系統硬限制是Linux系統在啟動時根據系統硬件資源狀況計算出來的
最佳的最大同時打開文件數限制,如果沒有特殊需要,不應該修改此限制
。
要修改它,需要對 /etc/sysctl.conf 文件,增加一行內容:
fs.file-max = 1048576
保存成功后,需執行下面命令使之生效:
[root@localhost ~]# sysctl -p
2:端口數量受限
解決完文件句柄數量受限的問題后,就要解決IP端口數量受限的問題了。一般來說,對外提供請求的服務端不用考慮端口數量問題,只要監聽某一個端口即可。可客戶端
要模擬大量的用戶對服務端發起TCP請求,而每一個請求都需要一個端口,為了使一個客戶端盡可能地模擬更多的用戶,也就要使客戶端擁有更多可使用的端口。
由於端口為16進制,即最大端口數為2的16次方65536(0-65535)。在Linux系統里,1024以下端口只有超級管理員用戶(如root)才可以使用,普通用戶只能使用大於等於1024的端口值。
我們可以通過以下命令查看系統提供的默認的端口范圍:
[root@localhost ~]# cat /proc/sys/net/ipv4/ip_local_port_range 32768 61000
即只有61000-32768=28232個端口可以使用,即單個IP對外只能同時發送28232個TCP請求。
修改方法有以下2種:
1、執行以下命令:
echo "1024 65535"> /proc/sys/net/ipv4/ip_local_port_range
該方法立即生效,但重啟后會失效。
2、修改 /etc/sysctl.conf 文件,增加一行內容:
net.ipv4.ip_local_port_range = 1024 65535
保存成功后,需執行下面命令使之生效:
[root@localhost ~]# sysctl -p
修改成功后,可用端口即增加到65535-1024=64511個,即單個客戶端機器只能同時模擬64511個用戶。要想突破這個限制,只能給該客戶端增加IP地址,這樣即可相應成倍地增加可用IP:PORT數。
具體可參考yongboy的這篇文章。
http://www.blogjava.net/yongboy/archive/2013/04/09/397594.html
3:TCP參數調優
要想提高服務端的性能,以達到我們高並發的目的,需要對系統的TCP參數進行適當的修改優化。
方法同樣是修改 /etc/sysctl.conf 文件,增加以下內容(修改后必須運行 sysctl -p 命令才會生效
):
net.ipv4.tcp_tw_reuse = 1 # 開啟重用,允許將 TIME_WAIT 套接字重新用於新的 TCP 連接 # 使用命令 cat /proc/sys/net/ipv4/tcp_tw_reuse 可以查看當前值!
當服務器需要在大量TCP連接之間切換時,會產生大量處於TIME_WAIT
狀態的連接。TIME_WAIT
意味着連接本身是關閉的,但資源還沒有釋放。將net_ipv4_tcp_tw_reuse
設置為1
是讓內核在安全時盡量回收連接,這比重新建立新連接要便宜得多。
net.ipv4.tcp_fin_timeout = 15 # 保持在FIN-WAIT-2狀態的時間 # 使用命令 cat /proc/sys/net/ipv4/tcp_fin_timeout 可以查看當前值!
這是處於TIME_WAIT
狀態的套接字在回收前必須等待的最小時間。改小它可以加快回收。
net.core.rmem_max = 16777216 net.core.wmem_max = 16777216
提高TCP的最大緩沖區大小,其中:
- net.core.rmem_max:表示接收套接字緩沖區大小的最大值(以字節為單位)。
- net.core.wmem_max:表示發送套接字緩沖區大小的最大值(以字節為單位)。
net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216
提高Linux內核自動對socket緩沖區進行優化的能力,其中:
- net.ipv4.tcp_rmem:用來配置讀緩沖的大小,第1個值為最小值,第2個值為默認值,第3個值為最大值。
- net.ipv4.tcp_wmem:用來配置寫緩沖的大小,第1個值為最小值,第2個值為默認值,第3個值為最大值。
net.ipv4.tcp_rmem用來配置讀緩沖的大小,三個值,第一個是這個讀緩沖的最小值,第三個是最大值,中間的是默認值。我們可以在程序中修改讀緩沖的大小,但是不能超過最小與最大。為了使每個socket所使用的內存數最小,我這里設置默認值為4096。
net.ipv4.tcp_wmem用來配置寫緩沖的大小。
讀緩沖與寫緩沖的大小,直接影響到socket在內核中內存的占用。
而net.ipv4.tcp_mem則是配置tcp的內存大小,其單位是頁,1頁等於4096字節。三個值分別為low, pressure, high
- ·low:當TCP使用了低於該值的內存頁面數時,TCP不會考慮釋放內存。
- ·pressure:當TCP使用了超過該值的內存頁面數量時,TCP試圖穩定其內存使用,進入pressure模式,當內存消耗低於low值時則退出pressure狀態。
- ·high:允許所有tcp sockets用於排隊緩沖數據報的頁面量,當內存占用超過此值,系統拒絕分配socket,后台日志輸出“TCP: too many of orphaned sockets”。
一般情況下這些值是在系統啟動時根據系統內存數量計算得到的。 根據當前tcp_mem最大內存頁面數是1864896,當內存為(18648964)/1024K=7284.75M時,系統將無法為新的socket連接分配內存,即TCP連接將被拒絕。實際測試環境中,據觀察大概在99萬個連接左右的時候(零頭不算),進程被殺死,觸發outof socket memory錯誤(dmesg命令查看獲得)。每一個連接大致占用7.5K內存(下面給出計算方式),大致可算的此時內存占用情況(990000 7.5/ 1024K = 7251M)。這樣和tcp_mem最大頁面值數量比較吻合,因此此值也需要修改。
另外net.ipv4.tcp_max_orphans這個值也要設置一下,這個值表示系統所能處理不屬於任何進程的 socket數量,當我們需要快速建立大量連接時,就需要關注下這個值了。當不屬於任何進程的socket的數量大於這個值時,dmesg就會看 到”too many of orphaned sockets”。
net.core.netdev_max_backlog = 4096
每個網絡接口接收數據包的速率比內核處理這些包的速率快時,允許送到隊列的數據包的最大數目。默認為1000。
net.core.somaxconn = 4096
表示socket監聽(listen)的backlog上限。什么是backlog呢?backlog就是socket的監聽隊列,當一個請求(request)尚未被處理或建立時,他會進入backlog。而socket server可以一次性處理backlog中的所有請求,處理后的請求不再位於監聽隊列中。當server處理請求較慢,以至於監聽隊列被填滿后,新來的請求會被拒絕。默認為128。
net.ipv4.tcp_max_syn_backlog = 20480
表示SYN隊列的長度,默認為1024,加大隊列長度為8192,可以容納更多等待連接的網絡連接數。
net.ipv4.tcp_syncookies = 1
表示開啟SYN Cookies。當出現SYN等待隊列溢出時,啟用cookies來處理,可防范少量SYN攻擊,默認為0,表示關閉。
net.ipv4.tcp_max_tw_buckets = 360000
表示系統同時保持TIME_WAIT套接字的最大數量,如果超過這個數字,TIME_WAIT套接字將立刻被清除並打印警告信息。默認為180000。
net.ipv4.tcp_no_metrics_save = 1
一個tcp連接關閉后,把這個連接曾經有的參數比如慢啟動門限snd_sthresh、擁塞窗口snd_cwnd,還有srtt等信息保存到dst_entry中,只要dst_entry沒有失效,下次新建立相同連接的時候就可以使用保存的參數來初始化這個連接。
net.ipv4.tcp_syn_retries = 2
表示在內核放棄建立連接之前發送SYN包的數量,默認為4。
net.ipv4.tcp_synack_retries = 2
表示在內核放棄連接之前發送SYN+ACK包的數量,默認為5。
完整的TCP參數調優配置如下所示:
net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 15 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216 net.core.netdev_max_backlog = 4096 net.core.somaxconn = 4096 net.ipv4.tcp_max_syn_backlog = 20480 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_tw_buckets = 360000 net.ipv4.tcp_no_metrics_save = 1 net.ipv4.tcp_syn_retries = 2 net.ipv4.tcp_synack_retries = 2
其它一些參數
vm.min_free_kbytes = 65536
用來確定系統開始回收內存的閥值,控制系統的空閑內存。值越高,內核越早開始回收內存,空閑內存越高。
vm.swappiness = 0
控制內核從物理內存移出進程,移到交換空間。該參數從0到100,當該參數=0,表示只要有可能就盡力避免交換進程移出物理內存;該參數=100,這告訴內核瘋狂的將數據移出物理內存移到swap緩存中。
存疑:
待考查?
net.ipv4.ip_conntrack_max = 1020000
net.ipv4.netfilter.ip_conntrack_max = 1020000
二、Nginx 優化
nginx配置文件優化
- nginx進程數,建議按照cpu數目來指定,一般為它的倍數: - worker_processes 4; - 為每個進程綁定cpu: - worker_cpu_affinity 00000001 00000010 00000100 00001000; - nginx進程打開的最多文件描述符數目: - worker_rlimit_nofile 102400; - 使用epoll的I/O復用模型: - use epoll; - 每個進程允許的最多連接數: - worker_connections 102400; - keepalive超時時間: - keepalive_timeout 60; - 客戶端請求頭部的緩沖區大小: (分頁大小可以用命令getconf PAGESIZE取得): - client_header_buffer_size 4k; - 打開文件指定緩存,默認是沒有啟用的,max指定緩存數量,建議和打開文件數一致,inactive是指經過多長時間文件沒被請求后刪除緩存: - open_file_cache max=102400 inactive=20s; - 指定多長時間檢查一次緩存的有效信息: - open_file_cache_valid 30s; - 設置最少使用次數,如果超過這個數字,文件描述符一直是在緩存中打開的: - open_file_cache_min_uses 1;
三、zuul 優化
一、突破 Tomcat 默認 200 並發連接上限
通過配置以下 tomcat 線程參數,可以提升並發連接上限:
server: tomcat: # 最大工作線程數,默認200, 4核8g內存,線程數經驗值800 # 操作系統做線程之間的切換調度是有系統開銷的,所以不是越多越好。 max-threads: 2000 # 目前起作用,測試到上萬都沒問題 # 最小工作空閑線程數,默認10, 適當增大一些,以便應對突然增長的訪問量 min-spare-threads: 100 # 總保持備用線程數 accept-count: 2000 # 等待隊列長度,默認100 max-connections: 2000 # 最大連接數,NIO默式默認 10000,其它為 maxThreads
一、accept-count:最大等待數
官方文檔的說明為:當所有的請求處理線程都在使用時,所能接收的連接請求的隊列的最大長度。當隊列已滿時,任何的連接請求都將被拒絕。
accept-count的默認值為100。
詳細的來說:當調用HTTP請求數達到tomcat的最大線程數時,還有新的HTTP請求到來,這時tomcat會將該請求放在等待隊列中,這個acceptCount就是指能夠接受的最大等待數,默認100。如果等待隊列也被放滿了,這個時候再來新的請求就會被tomcat拒絕(connection refused)。
二、maxThreads:最大線程數
每一次HTTP請求到達Web服務,tomcat都會創建一個線程來處理該請求,那么最大線程數決定了Web服務容器可以同時處理多少個請求。maxThreads默認200,肯定建議增加。但是,增加線程是有成本的,更多的線程,不僅僅會帶來更多的線程上下文切換成本,而且意味着帶來更多的內存消耗。JVM中默認情況下在創建新線程時會分配大小為1M的線程棧,所以,更多的線程異味着需要更多的內存。線程數的經驗值為:1核2g內存為200,線程數經驗值200;4核8g內存,線程數經驗值800。
三、maxConnections:最大連接數
官方文檔的說明為:
這個參數是指在同一時間,tomcat能夠接受的最大連接數。對於Java的阻塞式BIO,默認值是maxthreads
的值;如果在BIO模式使用定制的Executor執行器,默認值將是執行器中maxthreads
的值。對於Java 新的NIO模式,maxConnections 默認值是10000
。
對於windows上APR/native IO模式,maxConnections默認值為8192
,這是出於性能原因,如果配置的值不是1024的倍數,maxConnections 的實際值將減少到1024的最大倍數。
如果設置為-1,則禁用maxconnections功能,表示不限制tomcat容器的連接數。
maxConnections和accept-count的關系為:當連接數達到最大值maxConnections后,系統會繼續接收連接,但不會超過acceptCount的值。
1.4.3 圖解:maxConnections、maxThreads、acceptCount關系
用一個形象的比喻,通俗易懂的解釋一下tomcat的最大線程數(maxThreads)、最大等待數(acceptCount)和最大連接數(maxConnections)三者之間的關系。
我們可以把tomcat比做一個火鍋店,流程是取號、入座、叫服務員,可以做一下三個形象的類比:
- (1)acceptCount 最大等待數
可以類比為火鍋店的排號處能夠容納排號的最大數量;排號的數量不是無限制的,火鍋店的排號到了一定數據量之后,服務往往會說:已經客滿。 - (2)maxConnections 最大連接數
可以類比為火鍋店的大堂的餐桌數量,也就是可以就餐的桌數。如果所有的桌子都已經坐滿,則表示餐廳已滿,已經達到了服務的數量上線,不能再有顧客進入餐廳了。 - (3)maxThreads:最大線程數
可以類比為廚師的個數。每一個廚師,在同一時刻,只能給一張餐桌炒菜,就像極了JVM中的一條線程。
整個就餐的流程,大致如下:
- (1)取號:如果maxConnections連接數沒有滿,就不需要取號,因為還有空余的餐桌,直接被大堂服務員領上餐桌,點菜就餐即可。如果 maxConnections 連接數滿了,但是取號人數沒有達到 acceptCount,則取號成功。如果取號人數已達到acceptCount,則拿號失敗,會得到Tomcat的Connection refused connect 的回復信息。
- (2)上桌:如果有餐桌空出來了,表示maxConnections連接數沒有滿,排隊的人,可以進入大堂上桌就餐。
- (3)就餐:就餐需要廚師炒菜。廚師的數量,比顧客的數量,肯定會少一些。一個廚師一定需要給多張餐桌炒菜,如果就餐的人越多,廚師也會忙不過來。這時候就可以增加廚師,一增加到上限maxThreads的值,如果還是不夠,只能是拖慢每一張餐桌的上菜速度,這種情況,就是大家常見的“上一道菜吃光了,下一道菜還沒有上”尷尬場景。
maxConnections、maxThreads、acceptCount關系圖如下
Zuul 信號量上限配置(暫時不用,我們使用了熔斷器線程池)
zuul: semaphore: max-semaphores: 250 # 當Zuul的隔離策略為SEMAPHORE時, 設置默認最大信號量,會影響 ribbon 往后方調用
Zuul 默認每個路由的信號量是 100。
注:我們知道 Hystrix 有隔離策略
:THREAD 以及 SEMAPHORE ,默認是 SEMAPHORE 。
查詢資料發現是因為zuul默認每個路由直接用信號量做隔離,並且默認值是100,也就是當一個路由請求的信號量高於100那么就拒絕服務了,返回500。
線程池提供了比信號量更好的隔離機制,並且從實際測試發現高吞吐場景下可以完成更多的請求。但是信號量隔離的開銷更小,對於本身就是10ms以內的系統,顯然信號量更合適。
當 zuul.ribbonIsolationStrategy=THREAD時,Hystrix的線程隔離策略將會作用於所有路由。
此時,HystrixThreadPoolKey 默認為“RibbonCommand”。這意味着,所有路由的HystrixCommand都會在相同的Hystrix線程池中執行。可使用以下配置,讓每個路由使用獨立的線程池:
https://blog.csdn.net/w1014074794/article/details/88571880
Hystrix 熔斷器線程池配置
uul: ribbon-isolation-strategy: thread # threadPool: useSeparateThreadPools: true # 每個路由使用獨立的線程池,只有在隔離策略是thread才有效 hystrix: threadpool: default: core-size: 100 # 線程池核心線程數,它會影響總體的並發數,默認 10 maximumSize: 2000 # 此屬性設置最大線程池大小。這是可在拒絕命令執行的最大並發量。請注意, 如果您必須同時設置 allowMaximumSizeToDivergeFromCoreSize allowMaximumSizeToDivergeFromCoreSize: true #此屬性允許 maximumSize 的配置生效,如果maximumSize 大於 coreSize 配置,則在 keepAliveTimeMinutes 時間后回收線程 maxQueueSize: -1 #(最大排隊長度。默認-1,使用SynchronousQueue。其他值則使用 LinkedBlockingQueue。如果要從-1換成其他值則需重啟,即該值不能動態調整,若要動態調整,需要使用到下邊這個配置 command: default: execution: isolation: thread: timeoutInMilliseconds: 1000 # 熔斷超時時間,它和 ribbon 超時時間誰小以誰為准 hystrix: command: ms-consumer: execution: isolation: thread: timeoutInMilliseconds: 60000
Ribbon 上限配置
ms-consumer: ribbon: ConnectTimeout: 6000 # 與熔斷超時有關 ReadTimeout: 6000 #MaxTotalHttpConnections: 2000 MaxTotalConnections: 2000 MaxConnectionsPerHost: 2000
注意:如果配置了 MaxConnectionsPerHost 但是沒有配置 Zuul 信號量,默認並發會被限制在 100。
ribbon.ReadTimeout, ribbon.SocketTimeout這兩個就是ribbon超時時間設置,當在yml寫時,應該是沒有提示的,給人的感覺好像是不是這么配的一樣,其實不用管它,直接配上就生效了。
還有zuul.host.connect-timeout-millis, zuul.host.socket-timeout-millis這兩個配置,這兩個和上面的ribbon都是配超時的。區別在於,如果路由方式是serviceId的方式,那么ribbon的生效,如果是url的方式,則zuul.host開頭的生效。(此處重要!使用serviceId路由和url路由是不一樣的超時策略)
如果你在zuul配置了熔斷fallback的話,熔斷超時也要配置,不然如果你配置的ribbon超時時間大於熔斷的超時,那么會先走熔斷,相當於你配的ribbon超時就不生效了。
熔斷超時是這樣的:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
default代表默認
熔斷器降級觸發條件(知
#熔斷器失敗的個數==進入熔斷器的請求達到1000時服務降級(之后的請求直接進入熔斷器) hystrix.command.default.circuitBreaker.requestVolumeThreshold=1000
斷路器
(1)hystrix.command.default.circuitBreaker.requestVolumeThreshold(當在配置時間窗口內達到此數量的失敗后,進行短路。默認20個)
For example, if the value is 20, then if only 19 requests are received in the rolling window (say a window of 10 seconds) the circuit will not trip open even if all 19 failed.
簡言之,10s內請求失敗數量達到20個,斷路器開。
(2)hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds(短路多久以后開始嘗試是否恢復,默認5s)
(3)hystrix.command.default.circuitBreaker.errorThresholdPercentage(出錯百分比閾值,當達到此閾值后,開始短路。默認50%)
fallback
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests(調用線程允許請求HystrixCommand.GetFallback()的最大數量,默認10。超出時將會有異常拋出,注意:該項配置對於THREAD隔離模式也起作用)
https://blog.csdn.net/tongtong_use/article/details/78611225
consumer feign 配置
feign: client: config: default: connectTimeout: 20000 readTimeout: 20000 httpclient: enabled: true max-connections: 2000 # 默認值200 max-connections-per-route=: 2000 # 默認值為50 ribbon: MaxConnectionsPerHost: 2000 MaxTotalConnections: 2000 ConnectTimeout: 60000 # 請求連接的超時時間 默認的時間為 1 秒 ReadTimeout: 60000 # 請求處理的超時時間
Feign參數調優
在默認情況下 spring cloud feign在進行各個子服務之間的調用時,http組件使用的是jdk的HttpURLConnection,沒有使用線程池。
xxx 回退最大線程數 hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=50 #核心線程池數量 hystrix.threadpool.default.coreSize=130 #請求處理的超時時間 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=100000
SpringBoot 關於Feign的超時時間配置
無獨有偶,我今天也遇到了一個關於 feign 超時時間配置的問題。
今天項目現場提過來一個問題 “公司發過來的封裝好的 springboot 項目中的 feign 調用超時時間需要修改”,細問之后,具體的需求場景是這樣的:
1、首先要對 feign 的超時時間做設置
2、然后具體的要求是,只要對某一個微服務的其中一個接口進行特殊配置,對其余的所有接口做一個統一配置
公司 feign 版本 spring-cloud-starter-openfeign 2.2.3.RELEASE ,其他版本自行嘗試
基於 @FeignClient 的聲明式接口調用
一個實際的代碼示例:
@FeignClient(name = "${microservice.servicename.id:shanhy-id}", path = "/${microservice.servicename.id:shanhy-id}", url = "${microservice.serviceurl.id:}") public interface IdFeignClient { (代碼略) }
順着這個問題,我思考了一下,扒了下 feign 調用的相關源碼,下面直接給出結論(因為比較忙時間有限這里就不做源碼分析了):
1、feign 調用與超時有關的參數分為 連接超時時間 connect-timeout 和 讀取超時時間read-timeout
2、這兩個參數的默認值分別為 10秒 和 60秒
3、如果要對這兩個參數進行配置,那么對應的配置方法如下
feign.client.config.default.connect-timeout=5000 feign.client.config.default.read-timeout=30000
(單位毫秒)
4、當前代碼工程中有好幾個 @FeignClient 聲明,分別調用了不同的其他服務,如果要單獨為這個 shanhy-id 服務設置這兩個超時時間,那么對應的配置方法如下:
feign.client.config.shanhy-id.connect-timeout=2000 feign.client.config.shanhy-id.read-timeout=5000
注意和默認的區別就是中間那一段,將 default 替換為 shanhy-id,這個和 @FeignClient 中的 name 屬性一致
5、如果需要針對某一個服務中的某一個或幾個接口做特殊配置,那么就為這個特殊接口單獨寫一個 @FeignClient 接口定義,並為設置一個 contextId 設置一個和 name 不重名的名字,保證唯一,下面是例子:
@FeignClient(name = "${microservice.servicename.id:shanhy-id}", path = "/${microservice.servicename.id:shanhy-id}", url = "${microservice.serviceurl.id:}", contextId = "shanhy-id-2" ) public interface IdFeignClient { (代碼略) }
然后對應的配置為:
feign.client.config.shanhy-id-2.connect-timeout=3000 feign.client.config.shanhy-id-2.read-timeout=15000
其實系統啟動的時候,會為每一個 @FeignClient 定義的接口形成代理類然后進行配置,contextId 是當前 FeignClient 相關參數在 FeignContext 上下文中的 key,通過 contextId 來區分不同 FeignClient 的配置,如果 contextId 沒有配置則使用 name 作為上限文中的 key,與超時時間之外的其他相關配置詳見 FeignClientConfiguration
至此,問題解決。
問題:
The Hystrix timeout of 60000ms for the command service-basic is set lower than the combination of the Ribbon read and connect timeout, 240000ms。
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=60000
請求處理的超時時間,單位為毫秒
ribbon.ReadTimeout=60000
請求連接的超時時間,單位為毫秒
ribbon.ConnectTimeout=60000
對當前實例的重試次數,默認為0
ribbon.MaxAutoRetries=0
切換實例的重試次數,默認為0
ribbon.MaxAutoRetriesNextServer=1
我原本的配置為:
這句話的意思是:你熔斷器(Hystrix )的時間小於,負載均衡(Ribbon )的超時時間了
具體的超時時間排序應該為:
具體服務的hytrix超時時間 > 默認的hytrix超時時間 > ribbon超時時間
其中ribbon的計算方法為:
ribbonTimeout = (60000 + 60000) * (0 + 1) * (1 + 1) = 240000
所以如果默認的或者體服務的hytrix超時時間小於ribbon超時時間就會警告
將Hystrix超時時間設置超過240000即可。
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1800000 #請求處理的超時時間,單位為毫秒 ribbon.ReadTimeout=60000 #請求連接的超時時間,單位為毫秒 ribbon.ConnectTimeout=60000 #對當前實例的重試次數,默認為0 ribbon.MaxAutoRetries=0 #切換實例的重試次數,默認為0 ribbon.MaxAutoRetriesNextServer=1