Spring Cloud(五) --- zuul

来源:https://www.cnblogs.com/wadmwz/archive/2019/03/21/10563963.html
-Advertisement-
Play Games

微服務網關 在微服務架構中,後端服務往往不直接開放給調用端,而是通過一個API網關根據請求的url,路由到相應的服務。當添加API網關後,在第三方調用端和服務提供方之間就創建了一面牆,這面牆直接與調用方通信進行許可權控制,後將請求均衡分發給後臺服務端。 為什麼需要API Gateway 1. 簡化客戶 ...


微服務網關

在微服務架構中,後端服務往往不直接開放給調用端,而是通過一個API網關根據請求的url,路由到相應的服務。當添加API網關後,在第三方調用端和服務提供方之間就創建了一面牆,這面牆直接與調用方通信進行許可權控制,後將請求均衡分發給後臺服務端。

為什麼需要API Gateway

1. 簡化客戶端調用複雜度

在微服務架構模式下後端服務的實例數一般是動態的,對於客戶端而言很難發現動態改變的服務實例的訪問地址信息。因此在基於微服務的項目中為了簡化前端的調用邏輯,通常會引入 API Gateway 作為輕量級網關,同時 API Gateway 中也會實現相關的認證邏輯從而簡化內部服務之間相互調用的複雜度。

2. 數據裁剪以及聚合

通常而言不同的客戶端對於顯示時對於數據的需求是不一致的,比如手機端或者 Web 端又或者在低延遲的網路環境或者高延遲的網路環境。

因此為了優化客戶端的使用體驗,API Gateway 可以對通用性的響應數據進行裁剪以適應不同客戶端的使用需求。同時還可以將多個 API 調用邏輯進行聚合,從而減少客戶端的請求數,優化客戶端用戶體驗.

3. 多渠道支持

當然我們還可以針對不同的渠道和客戶端提供不同的API Gateway,對於該模式的使用由另外一個大家熟知的方式叫Backend for front-end, 在Backend for front-end模式當中,我們可以針對不同的客戶端分別創建其BFF,進一步瞭解BFF可以參考這篇文章:Pattern: Backends For Frontends

4. 遺留系統的微服務化改造

對於系統而言進行微服務改造通常是由於原有的系統存在或多或少的問題,比如技術債務,代碼質量,可維護性,可擴展性等等。API Gateway的模式同樣適用於這一類遺留系統的改造,通過微服務化的改造逐步實現對原有系統中的問題的修複,從而提升對於原有業務響應力的提升。通過引入抽象層,逐步使用新的實現替換舊的實現。

在Spring Cloud體系中, Spring Cloud Zuul就是提供負載均衡、反向代理、許可權認證的一個API gateway。

Spring Cloud Zuul

Spring Cloud Zuul路由是微服務架構的不可或缺的一部分,提供動態路由,監控,彈性,安全等的邊緣服務。Zuul是Netflix出品的一個基於JVM路由和服務端的負載均衡器。

使用代碼示例進行說明,這裡直接將gateway服務化.

  1. 創建項目cloud-zuul-server-demo

引入依賴:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

配置文件:

spring.application.name=zuul-server
server.port=8888

eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

啟動類添加註解:

@SpringBootApplication
@EnableZuulProxy

public class ZuulServerDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulServerDemoApplication.class, args);
    }

}

這樣zuul就搭建成功了

和前面的項目進行結合,啟動前面的註冊中心和eureka的兩個提供者,再將zuul-server啟動,這樣就可以進行測試了.訪問localhost:8888/eureka-service-producter/hello?name=wangzhi,這樣就是通過zuul來訪問提供者,並且多刷新幾次,可以看到實現了負載均衡,均勻出現. 感興趣的可以將消費者也啟動一下,然後訪問消費者看看會出現什麼情況.額外說一下前面路徑中的eureka-service-producter其實就是訪問項目的spring.applicaiton.name,也就是註冊中心可以看到的application,這個看清楚就沒問題.

到這裡,zuul網關的使用和自動轉發機制就完成了,下麵說說zuul相對的高級知識.

Zuul的核心

Filter是Zuul的核心,用來實現對外服務的控制。Filter的生命周期有4個,分別是“PRE”、“ROUTING”、“POST”、“ERROR”,整個生命周期可以用下圖來表示。

Zuul大部分功能都是通過過濾器來實現的,這些過濾器類型對應於請求的典型生命周期。

  • PRE: 這種過濾器在請求被路由之前調用。我們可利用這種過濾器實現身份驗證、在集群中選擇請求的微服務、記錄調試信息等。
  • ROUTING:這種過濾器將請求路由到微服務。這種過濾器用於構建發送給微服務的請求,並使用Apache HttpClient或Netfilx Ribbon請求微服務。
  • POST:這種過濾器在路由到微服務以後執行。這種過濾器可用來為響應添加標準的HTTP Header、收集統計信息和指標、將響應從微服務發送給客戶端等。
  • ERROR:在其他階段發生錯誤時執行該過濾器。 除了預設的過濾器類型,Zuul還允許我們創建自定義的過濾器類型。例如,我們可以定製一種STATIC類型的過濾器,直接在Zuul中生成響應,而不將請求轉發到後端的微服務。

Zuul中預設實現的Filter

類型 順序 過濾器 功能
pre -3 ServletDetectionFilter 標記助理server的類型
pre -2 Servlet30WrapperFilter 包裝HttpServletRequest請求
pre -1 FormBodyWrapperFilter 包裝請求體
route 1 DebugFilter 標記調試標誌
route 5 PreDecorationFilter 處理請求上下文供後續使用
route 10 RibbonRoutingFilter serviceId請求轉發
route 100 SimpleHostRoutingFilter url請求轉發
route 500 SendForwardFilter forward請求轉發
post 0 SendErrorFilter 處理有錯誤的請求響應
post 1000 SendResponseFilter 處理正常的請求響應

對於預設的過濾器,我們可以禁用的,在配置文件中添加(不過一般沒有取消的情況):

zuul.FormBodyWrapperFilter.pre.disable=true

自定義filter

其實也很簡單,繼承ZuulFilter就可以了,重寫四個方法,但是要瞭解個四個方法的返回值分別表示的是什麼意思?

public class MyFilter extends ZuulFilter {

    /**
     * 定義filter的類型,有pre、route、post、error四種
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 定義filter的順序,數字越小表示順序越高,越先執行
     * @return
     */
    @Override
    public int filterOrder() {
        return 10;
    }

    /**
     * 表示是否需要執行該filter,true表示執行,false表示不執行
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * filter需要執行的具體操作,比如說許可權認證,過濾等等
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        return null;
    }
}

舉個例子:

public class TokenFilter extends ZuulFilter {

    private final Logger logger = LoggerFactory.getLogger(TokenFilter.class);

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();

        logger.info("--->>> TokenFilter {},{}", request.getMethod(), request.getRequestURL().toString());

        // 獲取請求參數信息
        String token = request.getParameter("token");
        if (StringUtils.isNotBlank(token)) {
            //對請求進行路由
            context.setSendZuulResponse(true);
            context.setResponseStatusCode(200);
            context.set("isSuccess", true);
            return null;
        } else {
            //不對其進行路由
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(400);
            context.setResponseBody("token is empty");
            context.set("isSuccess", false);
            return null;
        }
    }
}

要想在過濾器中起作用,還需要額外配置一個,在啟動類中添加過濾器的Bean

@Bean
    public TokenFilter tokenFilter(){
        return new TokenFilter();
    }

啟動註冊中心,提供者和修改後的zuul,這次訪問的時候就需要在路徑後面加上token=xx,否則請求會被攔截.“PRE”類型的Filter做很多的驗證工作,在實際使用中我們可以結合shiro、oauth2.0等技術去做鑒權、驗證。

路由熔斷

我們的後端服務出現異常的時候,我們不希望將異常拋出給最外層,期望服務可以自動進行一降級。Zuul給我們提供了這樣的支持。當某個服務出現異常時,直接返回我們預設的信息。

我們通過自定義的fallback方法,並且將其指定給某個route來實現該route訪問出問題的熔斷處理。主要繼承ZuulFallbackProvider介面來實現,ZuulFallbackProvider預設有兩個方法,一個用來指明熔斷攔截哪個服務,一個定製返回內容。

public interface ZuulFallbackProvider {
   /**
     * The route this fallback will be used for.
     * @return The route the fallback will be used for.
     */
    public String getRoute();

    /**
     * Provides a fallback response.
     * @return The fallback response.
     */
    public ClientHttpResponse fallbackResponse();
}

實現類通過實現getRoute方法,告訴Zuul它是負責哪個route定義的熔斷。而fallbackResponse方法則是告訴 Zuul 斷路出現時,它會提供一個什麼返回值來處理請求。後來Spring又擴展了此類,豐富了返回方式,在返回的內容中添加了異常信息,因此最新版本建議直接繼承類FallbackProvider。

案例:修改zuul-server-demo
創建HelloFallback

@Component
public class HelloFallback implements FallbackProvider {

    private final Logger logger = LoggerFactory.getLogger(FallbackProvider.class);

    /**
     * 返回指定要處理的 service。
     * @return
     */
    @Override
    public String getRoute() {
        return "eureka-service-producter";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        if (cause != null && cause.getCause() != null) {
            String reason = cause.getCause().getMessage();
            logger.info("Excption {}",reason);
        }
        return fallbackResponse();
    }

    public ClientHttpResponse fallbackResponse() {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("The service is unavailable.".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

進行測試,啟動註冊中心,兩個提供者,zuul. 輸入http://localhost:8888/eureka-service-producter/hello?name=wangzhi&token=wangzhi進行訪問,多刷新幾次可以看到沒有問題,這個時候將一個提供者關閉,再次刷新可以看到問題就出來了,返回The service is unavailable. 這個就是zuul的路由熔斷. 但是註意的是: ** Zuul 目前只支持服務級別的熔斷,不支持具體到某個URL進行熔斷。**

路由重試

有時候因為網路或者其它原因,服務可能會暫時的不可用,這個時候我們希望可以再次對服務進行重試,Zuul也幫我們實現了此功能,需要結合Spring Retry 一起來實現。下麵我們以上面的項目為例做演示。

  1. 改動zuul項目
添加依賴:

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

添加配置:

#是否開啟重試功能
zuul.retryable=true
#對當前服務的重試次數
ribbon.MaxAutoRetries=2
#切換相同Server的次數
ribbon.MaxAutoRetriesNextServer=0
  1. 改動一個提供者項目
修改controller方法就好,只修改一個項目的就好

private Logger logger = Logger.getLogger(HelloController.class);

@GetMapping("hello")
public String hello(@RequestParam String name){
    logger.info("request two name is "+name);
    try{
        Thread.sleep(1000000);
    }catch ( Exception e){
        logger.error(" hello two error",e);
    }
    return "hello," + name + "222222";
}

這次訪問,雖然還會返回The service is unavailable.但是可以在改動的提供者項目的控制台看到日誌列印了3遍,說明重試了兩次.

註意

開啟重試在某些情況下是有問題的,比如當壓力過大,一個實例停止響應時,路由將流量轉到另一個實例,很有可能導致最終所有的實例全被壓垮。說到底,斷路器的其中一個作用就是防止故障或者壓力擴散。用了retry,斷路器就只有在該服務的所有實例都無法運作的情況下才能起作用。這種時候,斷路器的形式更像是提供一種友好的錯誤信息,或者假裝服務正常運行的假象給使用者。

不用retry,僅使用負載均衡和熔斷,就必須考慮到是否能夠接受單個服務實例關閉和eureka刷新服務列表之間帶來的短時間的熔斷。如果可以接受,就無需使用retry。

對於微服務來說,zuul也是很重要的,所以在必要情況下應該實現高可用,也就是搭建集群,和前面搭建集群一樣道理沒有什麼區別.為了保證Zuul的高可用性,前端可以同時啟動多個Zuul實例進行負載,在Zuul的前端使用Nginx或者F5進行負載轉發以達到高可用性。

上面就是zuul的全部內容的,以後有需要再進行補充.


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. 前言 技術這東西吧,看別人寫的好像很簡單似的,到自己去寫的時候就各種問題,“一看就會,一做就錯”。網上關於實現SSO的文章一大堆,但是當你真的照著寫的時候就會發現根本不是那麼回事兒,簡直讓人抓狂,尤其是對於我這樣的菜鳥。幾經曲折,終於搞定了,決定記錄下來,以便後續查看。先來看一下效果 2. 準 ...
  • 題意 "題目鏈接" Sol $B(x) = \exp(K\ln(A(x)))$ 做完了。。。 複雜度$O(n\log n)$ cpp // luogu judger enable o2 // luogu judger enable o2 include define Pair pair define ...
  • 聲明:此文章非本人所 原創,是別人分享所得,如有知道原作者是誰可以聯繫本人,如有轉載請加上此段話 1、請解釋一下什麼是 Nginx? Nginx是一個 web伺服器和反向代理伺服器,用於 HTTP、HTTPS、SMTP、POP3和 IMAP協議。 2、請列舉 Nginx的一些特性。 Nginx伺服器 ...
  • >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>歡迎轉載,轉載請註明出處-VirgoArt,www.cnblogs.com 一、場景描述: 主項目(Web)部署在Windows下,演算法項目(TensorFlow)部署在Linux環境下。 二、依賴環境(Jar) 三、後端代碼 ...
  • 1.圖片的上傳前臺三個條件 文件上傳頁面的3個要求: < 1.表單提交為post。mothod="post" < 2.表單的提交類型為enctype.必須是多段式。enctype="multipart/form-data" < 3.文件上傳用<input type="file"/>組件 。 2.圖片 ...
  • 說到 Spring Cloud,那肯定要少不了提一下微服務框架,所謂的微服務框架就是把負責的功能拆分成比較小、功能比較單一的服務獨立處理,例如單點登錄服務、支付服務、訂單服務等,當然如果訂單功能比較複雜還可以獨立出更具體的服務。 我在之前的文章中 "我所理解的SOA和微服務" 也提到過微服務的幾個主 ...
  • 爬取背景 這套課程雖然叫爬蟲入門類課程,但是裡面涉及到的點是非常多,十分檢驗你的基礎掌握的牢固程度,代碼中的很多地方都是可以細細品味的。 為什麼要寫這麼一個小東東呢,因為我生活在大河北,那霧霾醇厚的很,去了趟三亞,那空氣,嘖嘖,舒服的很,所以爬取一下三亞天氣,看看什麼時候去最好,理想的溫度為24~2 ...
  • 步驟一:下載JDK https://www.oracle.com 我們首先進入到這個網站下載我們運行JAVA的所必須的JDK 進入到這個網站按照圖示進入到下一個頁面 因為剛學JAVA,我們的JDK版本不宜過高,所以我們下載8.0版本 點擊Download下載之後首先勾選(如圖),再根據圖示下載JDK ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...