Ribbon——超時與重試


原文: 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 balanced RestTemplate, 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-balanced RestTemplates, 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在這里進行了兩個分支判斷

  1. 判斷isOkToRetryOnAllOperations=true時, 返回RetryHandler實現(xiàn)
  2. 判斷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.MaxAutoRetriesribbon.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方法涉及資源操作, 無法滿足冪等, 那么不建議啟用重試機制
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容