天天看點

Spring clould 調優

本文針對公司微服務并發的實際場景以及網上調研的資料,記錄影響微服務并發的各種優化配置。

先說明線上調用的實際例子:

通過zuul網關 調用服務A的接口,服務A的接口裡面通過Feign調用服務B的接口。

問題:

通過JMeter并發測試發現,并發數竟然沒有達到30次/s,即QPS不到30。這顯然不合理。

備注:

TPS(吞吐量) 系統在機關時間内處理請求的數量。

QPS(每秒查詢率) 每秒的響應請求數

第一步:熔斷器并發調優

首先想到的是Feign調用并發過大,導緻的熔斷問題,優化服務A中的熔斷配置

hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests 如果并發數達到該設定值,請求會被拒絕和抛出異常并且fallback不會被調用。預設10

果然,hystrix在semaphore隔離方案下,最大的并發預設是10。

優化配置:

# 線程政策
hystrix.command.default.execution.isolation.strategy=SEMAPHORE
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=500      

第二步:Zuul并發調優

經曆了将熔斷器執行線程并發設定為500後,繼續用JMeter進行并發測試,結果QPS到達100後,又出現大量請求失敗。

檢視日志,發現zuul很多請求連接配接關閉。

優化配置:

# zuul網關配置
zuul.semaphore.max-semaphores=500      

再次使用JMeter測試,發現并發500沒有在出現問題。

以上2個就是spring cloud并發調優最核心的2個參數。

下面系統說下spring cloud工程調優的問題

主要從以下幾個方面入手:

1、hystrix熔斷器并發調優

2、zuul網關的并發參數控制

3、Feign用戶端和連接配接數參數調優

4、Tomcat并發連接配接數調優

5、timeout逾時參數調優

6、JVM參數調優

7、ribbon和hystrix的請求逾時,重試以及幂等性配置

下面說明下具體調優參數:

hystrix熔斷器調優

表示HystrixCommand.run()的執行時的隔離政策,有以下兩種政策

1 THREAD: 在單獨的線程上執行,并發請求受線程池中的線程數限制

2 SEMAPHORE: 在調用線程上執行,并發請求量受信号量計數限制

在預設情況下,推薦HystrixCommands 使用 thread 隔離政策,HystrixObservableCommand 使用 semaphore 隔離政策。

隻有在高并發(單個執行個體每秒達到幾百個調用)的調用時,才需要修改HystrixCommands 的隔離政策為semaphore 。semaphore 隔離政策通常隻用于非網絡調用。

說明:高并發時,優先使用semaphore 。

hystrix.threadpool.default.coreSize=10
hystrix.threadpool.default.maximumSize=10
hystrix.threadpool.default.maxQueueSize=-1      

#如該值為-1,那麼使用的是SynchronousQueue,否則使用的是LinkedBlockingQueue。注意,修改MQ的類型需要重新開機。例如從-1修改為100,需要重新開機,因為使用的Queue類型發生了變化

如果想對特定的 HystrixThreadPoolKey 進行配置,則将 default 改為 HystrixThreadPoolKey 即可。

如果隔離政策是SEMAPHORE:

hystrix.command.default.execution.isolation.strategy=SEMAPHORE
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=10# 預設值      

如果想對指定的 HystrixCommandKey 進行配置,則将 default 改為 HystrixCommandKey 即可。

zuul參數控制

我們知道Hystrix有隔離政策:THREAD 以及SEMAPHORE ,預設是 SEMAPHORE 。

查詢資料發現是因為zuul預設每個路由直接用信号量做隔離,并且預設值是100,也就是當一個路由請求的信号量高于100那麼就拒絕服務了,傳回500。

線程池提供了比信号量更好的隔離機制,并且從實際測試發現高吞吐場景下可以完成更多的請求。但是信号量隔離的開銷更小,對于本身就是10ms以内的系統,顯然信号量更合适。

當 zuul.ribbonIsolationStrategy=THREAD時,Hystrix的線程隔離政策将會作用于所有路由。

此時,HystrixThreadPoolKey 預設為“RibbonCommand”。這意味着,所有路由的HystrixCommand都會在相同的Hystrix線程池中執行。可使用以下配置,讓每個路由使用獨立的線程池:

zuul:
  threadPool:
    useSeparateThreadPools: true      

隻有在隔離政策是thread才有效

  1. 隔離政策
zuul.ribbon-isolation-strategy=thread      
  1. 最大信号

    當Zuul的隔離政策為SEMAPHORE時:

    全局設定預設最大信号量:

zuul.ribbon-isolation-strategy=Semaphore
zuul:
  semaphore:
    max-semaphores: 100 # 預設值      

對路由linkflow和oauth單獨設定最大信号量

routes:
    linkflow:
      path: /api1/**
      serviceId: lf
      stripPrefix: false
      semaphore:
        maxSemaphores: 2000
    oauth:
      path: /api2/**
      serviceId: lf
      stripPrefix: false
      semaphore:
        maxSemaphores: 1000      

3.zuul并發連接配接參數

針對url的路由配置

zuul:
  host:
    max-total-connections: 200 # 預設值
    max-per-route-connections: 20 # 預設值      

針對serviceId的路由配置

serviceId:
  ribbon:
    MaxTotalConnections: 0   # 預設值
    MaxConnectionsPerHost: 0 # 預設值      

Feign參數調優

在預設情況下 spring cloud feign在進行各個子服務之間的調用時,http元件使用的是jdk的HttpURLConnection,沒有使用線程池。本文先從源碼分析feign的http元件對象生成的過程,然後通過為feign配置http線程池優化調用效率。

有種可選的線程池:HttpClient和OKHttp

個人比較推薦OKHttp,請求封裝的非常簡單易用,性能也很ok。

當使用HttpClient時,可如下設定:

feign.httpclient.enabled=true
feign.httpclient.max-connections=200# 預設值
feign.httpclient.max-connections-per-route=50# 預設值      

代碼詳見:

org.springframework.cloud.netflix.feign.FeignAutoConfiguration.

HttpClientFeignConfiguration#connectionManager

org.springframework.cloud.netflix.feign.ribbon.HttpClientFeignLoadBalancedConfiguration.

HttpClientFeignConfiguration#connectionManager

當使用OKHttp時,可如下設定:

feign.okhttp.enabled=true

feign.okhttp.max-connections=200# 預設值

feign.okhttp.max-connections-per-route=50# 預設值

1

2

3

代碼詳見:

org.springframework.cloud.netflix.feign.FeignAutoConfiguration.

OkHttpFeignConfiguration#httpClientConnectionPool 。

org.springframework.cloud.netflix.feign.ribbon.OkHttpFeignLoadBalancedConfiguration.

OkHttpFeignConfiguration#httpClientConnectionPool      

tomcat調優

如果使用的是内嵌的tomcat保持預設就好

server.tomcat.max-connections=0 # Maximum number of connections that the server accepts and processes at any given time.
server.tomcat.max-http-header-size=0 # Maximum size, in bytes, of the HTTP message header.
server.tomcat.max-http-post-size=0 # Maximum size, in bytes, of the HTTP post content.
server.tomcat.max-threads=0 # Maximum number of worker threads.
server.tomcat.min-spare-threads=0 # Minimum number of worker threads.      

由于預設的最大連接配接數,最大線程數都是0,沒有限制,是以在spring boot中啟動内嵌的tomcat,一般保持預設的配置就可以了。

JVM參數調優

關于Jvm調優Oracle官網有一份指導說明:

​​​Oracle官網對Jvm調優的說明​​ 有興趣大家可以去看看。

執行啟動設定Jvm參數的操作。

java   -Xms1024m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m  -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC    -jar   user-1.0.0.jar      

關于這些設定的JVM參數是什麼意思,請參考第二步中的oracle官方給出的調優文檔。

我在這邊簡單說一下:

-XX:MetaspaceSize=128m (元空間預設大小)
-XX:MaxMetaspaceSize=128m (元空間最大大小)
-Xms1024m (堆最大大小)
-Xmx1024m (堆預設大小)
-Xmn256m (新生代大小)
-Xss256k (棧最大深度大小)
-XX:SurvivorRatio=8 (新生代分區比例 8:2)
-XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,這裡使用CMS收集器)
-XX:+PrintGCDetails (列印詳細的GC日志)      

請求的逾時,重試,以及幂等性

#配置首台伺服器重試1次
ribbon.MaxAutoRetries=1
##配置其他伺服器重試1次
ribbon.MaxAutoRetriesNextServer=1
##擷取連接配接的逾時時間
ribbon.ConnectTimeout=1000
###請求處理時間
ribbon.ReadTimeout=1000
##每個操作都開啟重試機制
ribbon.OkToRetryOnAllOperations=true

#開啟Feign請求壓縮
feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
feign.compression.response.enabled=true
#配置斷路器逾時時間,預設是1000(1秒)
feign.hystrix.enabled=true
#feign use okhttp
feign.httpclient.enabled=false
feign.okhttp.enabled=true
#是否開啟逾時熔斷, 如果為false, 則熔斷機制隻在服務不可用時開啟
hystrix.command.default.execution.timeout.enabled=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000      

請求在1s内響應,超過1秒先同一個伺服器上重試1次,如果還是逾時或失敗,向其他服務上請求重試1次。

那麼整個ribbon請求過程的逾時時間為:

ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1);
ribbonTimeout = (1000 + 1000) * (1 + 1) * (1 + 1) = 8000      

由于Hystrix timeout一定要大于ribbonTimeout 逾時,是以

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds>8000      

逾時設定是為了防止某些耗時操作積壓線上程池中,導緻後續請求無法進行,壓爆伺服器。

重試是為了防止網絡抖動等原因出現偶然性異常的自動補償機制,不過這時一定要保證所有接口的幂等性。

Feign請求壓縮是為了減少網絡IO傳遞的耗時。