1. 程式人生 > >Spring Cloud學習系列第五篇【API網關服務】

Spring Cloud學習系列第五篇【API網關服務】

proxy pack -c boolean 定制 能力 部分 線程 問題:

  這篇隨筆接著學習微服務中一個比較重要的組件API網關服務。當我們微服務架構完成後最終是要提供給外部訪問的,於是我們需要一個統一的訪問入口,能隱藏我們內部服務URL細節,這就有點像局域網裏那個網關的概念了,這是API網關服務就應運而生了。API網關作用有能為實現請求路由、負載均衡、校驗過濾等基礎功能,還能實現請求轉發的熔斷機制、服務集合等高級功能。補充下通常我們對外服務統一入口可以采用F5、Nginx等方式也能實現前面的請求路由與負載均衡,但是要實現後面功能了F5、Nginx就無能為力了吧,這就是API網關服務強大之處。在Spring Cloud體系中我們可以采用Spring Cloud Zuul搭建我們的API網關服務,Zuul能與Spring Cloud全家桶這些組件整合,通過這樣的設計也就能更好的實現無論是請求路由、負載均衡還是熔斷、服務機制了。

  接著以API網關的請求路由、過濾器、服務容錯三個方面學習,API網關的搭建就跳過了,因為它和Eureka一樣都是非常簡單即可搭建好的,可自行了解下。

一、請求路由

  服務路由配置

  由於Zuul與Eureka整合便可知道每個服務的請求地址,因此可通過zuul.routes.<service-name>=<path>配置路由,當訪問地址如http://127.0.0.1:7001/api-a/user/1,將會配置該路由股則並向對應服務轉發。

#凡是以/api-a/開頭的URL都轉發到服務名為service-a的應用
zuul.routes.service-a=/api-a/**

  默認路由規則

  Zuul在不配置的情況下是有默認路由規則的,可以免去我們逐一配置的麻煩。默認情況都會以服務名作為path配置對應的服務。比方說我們有個服務叫service-a,那Zuul默認就會創建如下的路由規則。

zuul.routes.service-a=/service-a/**

#如果不希望使用默認路由規則,我們可以使用zuul.ignored-servies=*取消zuul的默認路由

  自定義路由規則

  如果覺得默認路由規則不符合自己業務需求還可以通過自定義路由規則定義自己路由規則,還是舉個例子:服務名為service-a,默認路由規則是/service-a/**,但如果希望把默認路由規則變成/service/a/**可以在啟動類添加PatternServiceRouteMapper類,並且采用正則表達式命名分組去匹配服務名稱和定制自己的路由規則,如下例子,當服務名稱符合PatternServiceRouteMapper構造函數的第一個參數(?<part1>^.+)-(?<part2>.+$)這個正則表達式,並且獲取了命名分組part1和part2,然後就可以通過第二個參數去定義路由。當然如果不符合第一個參數的正則表達式則走默認路由規則。

package com.pumpkin;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.discovery.PatternServiceRouteMapper;
import org.springframework.context.annotation.Bean;

@EnableZuulProxy
@SpringCloudApplication
public class Application {

    public static void main(String[] args){
        new SpringApplicationBuilder(Application.class).web(true).run(args);
    }

    @Bean
    public PatternServiceRouteMapper serviceRouteMapper() {
        return new PatternServiceRouteMapper(
                "(?<part1>^.+)-(?<part2>.+$)",
                "${part1}/${part2}");
    }

}

  路徑匹配

  在定義路由是通常需要使用通配符的,Zuul的通配符采用了Ant風格定義,具體如下。需要註意的是Zuul的路由配置是根據順序配置而不是最長配置路徑匹配。也就是說一個URL區配置路由的時候,並且有多個路由可以匹配到,一旦找到第一個可以匹配上的路由就會結束下面的匹配了。

  技術分享圖片

  除此之外zuul還提供了忽略表達式用於忽略不希望被API網關進行路由的URL表達式,比方說service-a服務的/hello.do接口不希望被路由到,可以添加如下配置。

zuul.ignored-patterns=/**/hello.do/**

  本地跳轉

  在Zuul實現的API網關路由功能中,還支持forward形式的服務器端跳轉配置,就是說可以設置一個路由規則,符合該路由規則的講會轉發到zuul網關自身去處理,當然使用本地跳轉時,Zuul網關也有實現相應的接口。

#本地跳轉
#當我訪問http://127.0.0.1:7001/api-b-url/hello,將會轉發給給Zuul自身去處理 zuul.routes.api
-b-url.path=/api-b-url/** zuul.routes.api-b-url.url=forward:/local

  Cookie與頭信息

  默認情況下Zuul在處理請求時會過濾掉一些HTTP請求頭信息,默認的敏感頭信息通過zuul.sensitiveHeaders參數定義,包括Cookie、Set-Cookie、Authoriztion三個屬性,所以Web項目常用的Cookie默認情況下是不會傳遞的,為了解決這個問題,有如下幾個配置方法。

#通過設置全局參數為空覆蓋默認值
#zuul.sensitiveHeaders=
#對指定路由開啟自定義敏感頭
#zuul.routers.<router>.customSensitiveHeaders=true
#講指定路由的敏感頭設置為空
#zuul.routers.<router>.sensitiveHeaders=

  除了Cookie問題外,還有一個重定向問題需要了解的。由於Zuul沒有正確的處理HTTP請求頭信息中的Host,在某些需要重定向場景下會出現如下問題:比方說向zuul網關發送一個登錄請求,登錄成功後後端服務返回一個302重新定向響應,該響應的host頭信息可能是具體實例的IP和端口,此時瀏覽器可能看到的就是實例的IP而不是網關的IP了,如下圖。需要解決這個問題在Camden版本之後可以通過屬性zuul.add-host-header=true得到處理,不過基於目前我學習的這個版本的話是沒有提供直接的解決辦法的,需要自行參考Camden版本的PreDecorationFilter過濾器去處理。

技術分享圖片

二、過濾器

  Zuul過濾器應該可以說是Zuul最精華的一部分了,它主要提供了四種類型的過濾器,這四種過濾器基礎的的抽象類(ZuulFilter)是一樣的,但不同的過濾器在Zuul中作用不一樣,生命周期也是不一樣的,下面先看看ZuulFilter接抽象類幾個重要抽象方法。

   #這個屬性有四種類型對應不同的過濾器分別是pre、routing、posting、error
    public abstract java.lang.String filterType();
   #決定過濾器執行順序,數值越小優先級越高
    public abstract int filterOrder();
  #判斷過濾器是否要執行
    boolean shouldFilter();
  #過濾器執行邏輯
    java.lang.Object run();

  介紹四種過濾器

  通過ZuulFilter介紹基本就了解過濾器是怎麽個使用了吧,直接編寫一個類繼承ZuulFilter,指明該過濾器的類型、優先級、邏輯等然後被Spring去加載便可,那麽不同類型的過濾器生命周期和作用是什麽?下面一張官方大圖展示的比較清楚了。

  pre過濾器:在請求路由前被調用,大家都熟知的這個階段適合做一些校驗邏輯

  routing過濾器:在路由請求時被調用

  post過濾器:在routing和error過濾器之後被調用,這個類型的過期就一般用於對請求響應結果做一些加工

  error:處理請求時發生錯誤被調用

  在Spring Cloud Zuul中已經是有一批默認的實現了的核心過濾器了,它們會在Zuul啟動的時候自動加載和啟用,從圖中可以看到一個有趣的路徑是error過濾器在任一類型的過濾器發生異常時都會進入,但它最終也還是會進入到post過濾器返回請求給客戶端。

技術分享圖片

 

  禁用過濾器

  有時我們需要禁用zuul某些默認過濾器或自定義過濾器的需求,那麽可以如下

#其中<SimpleClassName>為簡單類名,<filterType>為過濾器#類型
zuul.<SimpleClassName>.<filterType>.disable=true
eg:
zuul.AccessFilter.pre.disable=true

  處理異常信息

  這是開發中常見需求,在<<Spring Cloud微服務實戰>>一書以及永超大神博客中有比較詳細的介紹,跳過~~

三、服務容錯

  既然是Spring Cloud的一員,那麽在服務容錯方面當然有Hystrix和Ribbon良好的支持啦。也就是Zuul天生就擁有線程隔離、斷路器、客戶端負載均衡的能力了,不過在設置路由的時候需要以zuul.routes.service-a=/api-a/**這種服務路由的方式設置,否則將不具備服務容錯能力。而Hystrix和Ribbon的配置還是和原本一樣,簡單介紹下。

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds - 設置路由轉發請求執行總時間,超時會返回TIMEOUT錯誤

ribbon.ConnectTimeout - 設置請求創建連接時間

ribbon.ReadTimeout - 設置請求超時時間

ribbon.ConnectTimeout和ribbon.ReadTimeout 設置時間小於hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 時出現請求創建連接超時或者請求超時,ribbon都會進行重試路由請求的,如果我們不希望有這個重試機制可以通過
zuul.retryable=false - 全局關閉
或者
zuul.routers.<route>.retryable=false - 指定路由關閉

四、總結

  Api網關可以說是微服務閉環中不可或缺的一部分,今天對Spring Cloud Zuul相關內容進行了學習最大感受是Zuul其實是具有“微服務”特色的一個服務代理工具,正是這個特點使得它與Nginx和F5這些服務代理工具不一樣,要做的事情更多,例如檢驗、服務容錯、對請求結果進行加工等等,才能保證微服務可以得到穩定運行。

六、參考資料

Spring Cloud微服務實戰-翟永超。本系列的學習都是參考該書籍學習的,同時源碼使用的Spring Boot和Spring Cloud的版本也與該書保持一致。

七、源碼

碼雲地址:[email protected]:pumpkingg/Spring-Cloud-Study.git 該篇隨筆對應的代碼是master分支下命名為blog4的Tag

Spring Cloud學習系列第五篇【API網關服務】