(三十五) 跟我學習SpringCloud-Spring Cloud Zuul過濾器介紹及使用(傳遞數據、攔截請求和異常處理)

Zuul 主要功能就是轉發,在轉發過程中我們無法保證被轉發的服務是可用的,這個時候就需要容錯機制及回退機制。

容錯機制

容錯,簡單來說就是當某個服務不可用時,能夠切換到其他可用的服務上去,也就是需要有重試機制。在 Zuul 中開啓重試機制需要依賴 spring-retry。

首先在 pom.xml 中添加 spring-retry 的依賴,代碼如下所示。

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

在屬性文件中開啓重試機制以及配置重試次數:

zuul.retryable=true
ribbon.connectTimeout=500
ribbon.readTimeout=5000
ribbon.maxAutoRetries=1
ribbon.maxAutoRetriesNextServer=3
ribbon.okToRetryOnAllOperations=true
ribbon.retryableStatusCodes=500,404,502

其中:

  • zuul.retryable:開啓重試。
  • ribbon.connectTimeout:請求連接的超時時間(ms)。
  • ribbon.readTimeout:請求處理的超時時間(ms)。
  • ribbon.maxAutoRetries:對當前實例的重試次數。
  • ribbon.maxAutoRetriesNextServer:切換實例的最大重試次數。
  • ribbon.okToRetryOnAllOperations:對所有操作請求都進行重試。
  • ribbon.retryableStatusCodes:對指定的 Http 響應碼進行重試。


可以啓動兩個 hystrix-feign-demo 服務,默認 Ribbon 的轉發規則是輪詢,然後我們停掉一個 hystrix-feign-demo 服務。沒加重試機制之前,當你請求接口的時候肯定有一次是會被轉發到停掉的服務上去的,返回的是異常信息。

當我們加入了重試機制後,你可以循環請求接口,這個時候不會返回異常信息,因爲 Ribbon 會根據重試配置進行重試,當請求失敗後會將請求重新轉發到可用的服務上去。

回退機制

 Spring Cloud 中,Zuul 默認整合了 Hystrix,當後端服務異常時可以爲 Zuul 添加回退功能,返回默認的數據給客戶端。

實現回退機制需要實現 ZuulFallbackProvider 接口,代碼如下所示。

@Component
public class ServiceConsumerFallbackProvider implements ZuulFallbackProvider {
    private Logger log = LoggerFactory.getLogger(ServiceConsumerFallbackProvider.class);

    @Override
    public String getRoute() {
        return "*";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return this.getStatusCode().value();
            }

            @Override
            public String getStatusText() throws IOException {
                return this.getStatusCode().getReasonPhrase();
            }

            @Override
            public void close() {
            }

            @Override
            public InputStream getBody() throws IOException {
                if (cause != null) {
                    log.error("", cause.getCause());
                }
                RequestContext ctx = RequestContext.getCurrentContext();
                ResponseData data = ResponseData.fail("服務器內部錯誤 ", ResponseCode.SERVER_ERROR_CODE.getCode());
                return new ByteArrayInputStream(JsonUtils.toJson(data).getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                MediaType mt = new MediaType("application", "json", Charset.forName("UTF-8"));
                headers.setContentType(mt);
                return headers;
            }
        };
    }
}

getRoute 方法中返回*表示對所有服務進行回退操作,如果只想對某個服務進行回退,那麼就返回需要回退的服務名稱,這個名稱一定要是註冊到 Eureka 中的名稱。

通過 ClientHttpResponse 構造回退的內容。通過 getStatusCode 返回響應的狀態碼。通過 getStatusText 返回響應狀態碼對應的文本。通過 getBody 返回回退的內容。通過 getHeaders 返回響應的請求頭信息。

通過 API 網關來訪問 hystrix-feign-demo 服務,將 hystrix-feign-demo 服務停掉,然後再次訪問,就可以看到回退的內容了,如圖 1 所示。

Zuul 高可用

跟業務相關的服務我們都是註冊到 Eureka 中,通過 Ribbon 來進行負載均衡,服務可以通過水平擴展來實現高可用。

現實使用中,API 網關這層往往是給 APP、Webapp、客戶來調用接口的,如果我們將 Zuul 也註冊到 Eureka 中是達不到高可用的,因爲你不可能讓你的客戶也去操作你的註冊中心。

這時最好的辦法就是用額外的負載均衡器來實現 Zuul 的高可用,比如我們最常用的 Nginx,或者 HAProxy、F5 等。

這種方式也是單體項目最常用的負載方式,當用戶請求一個地址的時候,通過 Nginx 去做轉發,當一個服務掛掉的時候,Nginx 會把它排除掉。

如果想要 API 網關也能隨時水平擴展,那麼我們可以用腳本來動態修改 Nginx 的配置,通過腳本操作 Eureka,發現有新加入的網關服務或者下線的網關服務,直接修改 Nginx 的 upstream,然後通過重載(reload)配置來達到網關的動態擴容。

如果不用腳本結合註冊中心去做的話,就只能提前規劃好 N 個節點,然後手動配置上去。

給大家推薦分佈式架構源碼

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