原文: Ribbon——超時與重試
date: 2019-04-26 18:57:04
[TOC]
前言
在上篇源碼分析——客戶端負載Netflix Ribbon中提到了重試, 在Spring Cloud中各組件關(guān)于重試的概念還是很容易混淆
你會發(fā)現(xiàn)在Netflix OSS中: Eureka, Ribbon, Zuul, feign, etc... 雖然每個功能組件都很清晰, 能夠單獨使用
在Spring Cloud中看似也很清晰, 但實際上Spring Cloud的整合中會把多個組件配套組合起來, 以達到簡化分布式開發(fā)的目的
例如在深入實踐組件Feign時, 你是不是需要了解feign-hystrix, ribbon相關(guān)的東西呢?
再例如超時, Zuul, Feign, Hystrix還是RestTemplate, 它們都有timeout相關(guān)的概念, retry機制也類似...
還好關(guān)于各組件中的超時和重試, 已經(jīng)有人對其做了總結(jié), 附上原文鏈接:
_感謝這些分享的人
補充
由于原文中的總結(jié)比較簡單, 這里再對重試做一些補充
spring-retry
在Ribbon的重試中, 除了配置項, 還需要加入spring-retry
Retrying Failed Requests
Spring Cloud Netflix offers a variety of ways to make HTTP requests. You can use a load balancedRestTemplate, Ribbon, or Feign. No matter how you choose to create your HTTP requests, there is always a chance that a request may fail. When a request fails, you may want to have the request be retried automatically. To do so when using Sping Cloud Netflix, you need to include Spring Retry on your application’s classpath. When Spring Retry is present, load-balancedRestTemplates, Feign, and Zuul automatically retry any failed requests (assuming your configuration allows doing so).
- pom依賴
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
配置項 (int)
文中提及了三個配置項參數(shù):
ribbon:
# 同一實例最大重試次數(shù),不包括首次調(diào)用
MaxAutoRetries: 1
# 重試其他實例的最大重試次數(shù),不包括首次所選的server
MaxAutoRetriesNextServer: 2
# 是否所有操作都進行重試
OkToRetryOnAllOperations: true
在ribbon-core中可以找到對應(yīng)的默認值
public class DefaultClientConfigImpl implements IClientConfig {
// MaxAutoRetriesNextServer (retryNextServer:重試下一實例)
public static final int DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER = 1;
// MaxAutoRetries (retrySameServer:重試相同實例)
public static final int DEFAULT_MAX_AUTO_RETRIES = 0;
// OkToRetryOnAllOperations
public static final Boolean DEFAULT_OK_TO_RETRY_ON_ALL_OPERATIONS = Boolean.FALSE;
}
這里要理解三個參數(shù)的意義, 可以這樣推算出:
- 當(dāng)MaxAutoRetries=1, MaxAutoRetriesNextServer=2時:
RetryCount = (1+1) * (2+1) = 6次 - 當(dāng)MaxAutoRetries=0, MaxAutoRetriesNextServer=1時:
RetryCount = (0+1) * (1+1) = 2次
也就是: RetryCount = (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1)
配置項 (bool)
上面三個參數(shù)中還有一個bool參數(shù): OkToRetryOnAllOperations
文中的解釋是是否所有操作都進行重試, 這里的操作是指什么? 我第一眼看不懂
細節(jié)盡在源碼中:
@Override
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
RibbonRequest request, IClientConfig requestConfig) {
if (this.ribbon.isOkToRetryOnAllOperations()) {
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
requestConfig);
}
if (!request.toRequest().method().equals("GET")) {
return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(),
requestConfig);
}
else {
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
requestConfig);
}
}
這是源碼中的一段邏輯, 可以看到isOkToRetryOnAllOperations在這里進行了兩個分支判斷
- 判斷
isOkToRetryOnAllOperations=true時, 返回RetryHandler實現(xiàn) - 判斷HTTP Method
- GET: 返回RetryHandler實現(xiàn)
- !GET: 返回RetryHandler實現(xiàn), 不一樣的是其中二個bool值為False
可以看到, 它們都會返回一個RetryHandler實現(xiàn), 不同處在于傳入的參數(shù), 更準確的說是第二個bool值參數(shù)
來看下RequestSpecificRetryHandler構(gòu)造函數(shù)中的幾個參數(shù)
-
bool okToRetryOnConnectErrors: 字面意思是重試連接錯誤, 都為true -
bool okToRetryOnAllErrors: 字面意思是重試所有錯誤, 只有HTTP Method不是GET時為false - etc...
理解這兩個參數(shù), 你只需要能夠區(qū)分兩種常見的Exception, 即:
-
java.net.SocketTimeoutException: Read timed out: 這里的超時代表服務(wù)器請求超時 -
java.net.ConnectException: Connection refused: 連接被拒絕, 也就是無法連接到服務(wù)器
最大的區(qū)別在于SocketTimeoutException是服務(wù)端已經(jīng)接收到請求, 而客戶端沒有正常接收(例如超時了)
需要明白, 這里的重試是指目標(biāo)服務(wù)不可達或無法正常響應(yīng), 而不是指返回一個4xx, 5xx的錯誤重試
注意: OkToRetryOnAllOperations那里的HTTP Method判斷作用的是服務(wù)提供者的HTTP方法, 并非調(diào)用方本身
關(guān)于Open Feign
文中提到了Feign的重試中, 雖然Feign和Ribbon充實是獨立的, 如果都啟用可能會讓重試次數(shù)疊加
所以Spring Cloud在后來版本改為feign.Retryer#NEVER_RETRY, 即默認不開啟Feign的重試, 轉(zhuǎn)而可以使用Ribbon的重試配置即可
但是, 注意過Spring Cloud Finchley版的可能知道Feign的引入出現(xiàn)了變化, 變成了open-feign
重點有以下幾點
- OpenFeign有自己的重試機制
- spring-retry對于OpenFeign是不起作用的
- 但是OpenFeign中讀取的配置還是
ribbon.MaxAutoRetries和ribbon.MaxAutoRetriesNextServer, 依然生效
詳細及源碼分析可參考: Spring Cloud Finchley OpenFeign的重試配置相關(guān)的坑
使用建議
重試確實能提高服務(wù)的容錯和可用性, 但是使用不當(dāng)不如不要使用
1: Hystrix超時時間
Hystrix的超時時間必須大于超時的時間: 這點很容易理解, 如果小于超時時間, 那就熔斷了, 沒有機會重試了
2: 考慮服務(wù)冪等性
這點在判斷OkToRetryOnAllOperations邏輯的源碼那里也能看出來, 在啟用重試時一定要考慮下游服務(wù)接口的冪等性
概述為以下兩點:
- 如果你的服務(wù)API沒有完全考慮冪等性, 建議不要把OkToRetryOnAllOperations設(shè)置為True
- 其次, 即使設(shè)置為False, 如果下游服務(wù)的GET方法涉及資源操作, 無法滿足冪等, 那么不建議啟用重試機制