Spring clould 調優

轉至:https://blog.csdn.net/w1014074794/article/details/88571880

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

先說明線上調用的實際例子:
通過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傳遞的耗時。

你的系統架構中,只要涉及到了重試,那麼必須上接口的冪等性保障機制
否則的話,試想一下,你要是對一個接口重試了好幾次,結果人家重複插入了多條數據,該怎麼辦呢?
其實冪等性保證本身並不複雜,根據業務來,常見的方案:
可以在數據庫裏建一個唯一索引,插入數據的時候如果唯一索引衝突了就不會插入重複數據
或者是通過redis裏放一個唯一id值,然後每次要插入數據,都通過redis判斷一下,那個值如果已經存在了,那麼就不要插入重複數據了。
類似這樣的方案還有一些。總之,要保證一個接口被多次調用的時候,不能插入重複的數據。

更多資料:
https://blog.csdn.net/qq_30353203/article/details/76690272
https://www.cnblogs.com/xingzc/p/5778010.html
https://www.cnblogs.com/killerqi/p/10907344.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章