第6章 微服務的大門誰來守

目錄

6.1 使用Spring Cloud Zuul構建基礎網關

6.2 Zuul的一些常用配置

6.2.1 自定義請求路徑

6.2.2 配置靜態 URL 路由

6.2.3 服務超時

6.3 過濾器

6.4 小結


截止目前,我們已經構建了Eureka註冊中心,customer和order微服務,並且創建了配置中心,讓微服務可以遠程獲取配置中心的配置項,同時,我們還研究了微服務之間是如何不通過域名來進行服務間調用。本章我們要通過外部來調用微服務,在本章,我們要爲我們的微服務系統樹起一道大門,使用Spring Cloud Netflix的Zuul構建一道網關。

服務網關最基本的功能是用來作爲服務客戶端和被調用服務之間的中介,爲微服務系統添加網關,可以讓服務的客戶端請求只直接與網關服務進行交互對話,至於具體的服務接口則由網關根據配置規則進行自動選擇。有了網關服務,所有的客戶端請求都應該流經網關服務。

6.1 使用Spring Cloud Zuul構建基礎網關

首先,我們創建一個Spring Boot的工程,在pom文件中包含如下內容:

注意:這裏的spring-cloud.version是Greenwich.RELEASE。Finchley.SR2的zuul有Bug,可以自行修改測試下。

<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
    <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>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

</dependencies>
<dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>

</dependencyManagement>

在application.properties中添加下面的配置項:

Eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
spring.cloud.config.fail-fast=true
spring.application.name=gateway
server.port=8003
spring.cloud.config.uri=http://localhost:8888
spring.cloud.config.name=gateway
spring.cloud.config.profile=dev
management.endpoints.web.exposure.include=*
spring.main.allow-bean-definition-overriding=true

最後,在啓動類添加@EnableZuulProxy(另一個@EnableZuulServer註解只會創建一個Zuul服務器,不會加載Zuul的任何過濾器,本系列不涉及。),啓動項目。

啓動之前我們創建的customer,order,eureka-server。打開Eureka的註冊頁面。可以看到如圖6.1所示的信息。

1336be7bed6b33ea030b6449e4067527.png
圖6.1 加入了網關後的Eureka註冊頁面

網關作爲系統中的一個微服務存在。同時,它又可以獨立於其他微服務存在,作爲其他微服務的前置來進行服務訪問,將Zuul作爲一個微服務註冊道Eureka Server上,開發人員可以通過Eureka Server動態地進行服務實例的新增和刪除,在這個過程中,不需要對Zuul進行任何修改以及配置,Zuul在不斷與Eureka Server進行通訊的過程中,會獲取到最新的實例信息,並在Zuul中進行緩存。

我們之前在order中提供了接口/users/{userId},這個接口可以通過RIbbon進行服務間的訪問,並且返回用戶信息。直接通過order微服務訪問的話,完整地址是:http://localhost:8002/users/{userId}。現在我們要通過網關訪問這個接口,地址應該是http://localhost:8003/order/users/{userId}。瀏覽器中訪問,可以看到如圖6.2所示的結果。

6f469717c5693880abd11169e4733b7c.png
圖6.2 通過網關訪問order微服務接口

 

6.2 Zuul的一些常用配置

6.2.1 自定義請求路徑

當瀏覽器中輸入http://localhost:8003/order/users/100001時,Zuul能夠通過微服務在Eureka中註冊的instanceId訪問到對應的微服務。但有時候,我們會要求對這個服務名進行隱藏,比如用o代替order這個instanceId。這時候,只需要在配置文件中添加如下配置:

zuul.routes.order=/o/**

重啓網關,瀏覽器中輸入http://localhost:8003/o/users/10000,請求會被路由到order微服務的對應接口,可以看到如圖6.3所示的結果。

8bcaa99a3d9fc378e566f936d2ae6749.png
圖6.3 自定義請求路徑訪問

 

注意:這個時候,http://localhost:8003/order/users/100001
也是可以正常訪問的。同時,routes端點可以用來查看當前Zuul的路由規則,這個端點是Zuul獨有的。

同時,訪問routes端點可以看到網關當前的映射關係,如圖6.4所示。

681c593547ef18b75ea8d04b397fa30c.png
圖6.4 Zuul的routes端點

 

網關作爲多個微服務的前置服務,不但要對接收到的請求進行攔截、轉發,同時,還可以對某些特定的服務進行忽略,這個配置需要使用配置項zuul.ignored-services。我們在網關的配置文件中添加配置項:

zuul.ignored-services=order

重啓服務,查看網關服務的 routes 斷點,可以看到圖6.5的結果。

e1fb6119def6a4aad5ebe1a9379a5058.png
圖6.5 過濾掉 order 服務的默認路由配置

 

此時,http://localhost:8003/order/users/100001不能正常訪問,但是http://localhost:8003/o/users/100001可以正常獲取結果。

zuul.ignored-services可以直接配置成*,然後通過zuul.routes.xxx=yyy來配置生效的動態路由,這種方式類似於我們平常開發中的『白名單』。

最後,再介紹一個比較有用的配置項:zuul.prefix,這個配置主要用來爲服務調用添加前綴。比如我們想通過
http://localhost:8003/api/o/users/100001來訪問服務,那麼就可以在網關的配置文件中添加配置項:

zuul.prefix=/api

可以看到網關的routes 端點變成了圖6.6的樣子。

ca78f7a859d19d98ec9108b6d930f9fe.png
圖6.6 爲所有的網關調用添加接口前綴

 

6.2.2 配置靜態 URL 路由

還有一種比較常見的需求,在某些場景下,我們可能需要將某個請求路由到一個非本系統的固定地址上去,這個地址不註冊在本系統的
Eureka Server 上,所以不能通過前面講到的方式去配置。

比如我們要在請求 http://localhost:8003/api/baidu/xxx時路由到地址
https://www.baidu.com/s?wd=xxx。就可以使用本節要講到的靜態 URL
路由配置。在網關的配置文件中添加如下配置:

zuul.routes.user.path=/baidu/**
zuul.routes.user.url=http://localhost:8002/users/

重啓網關,配置生效後,查看網關routes端點,可以看到如圖6.7所示結果:

769011d1afb441042e29f772867c75cd.png
圖6.7 配置靜態URL 路由後查看 routes 端點

在瀏覽器地址欄輸入地址http://localhost:8003/api/user/123,會自動跳轉到http://localhost:8002/users/123。通過這樣的配置,就可以讓某類請求直接路由到對應的外部請求接口上。

6.2.3 服務超時

Zuul 使用了 Hystrix 和
Ribbon來防止長時間運行的服務調用影響到網關的整體性能。默認情況,對於任何一個需要運行超過1s的請求來說,會觸發 Hystrix 超時,調用將被禁止並且返回 HTTP 500的錯誤。當然,在某些時候,這個1s 有點短,我們就可以通過 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds來自定義Hystrix的超時時間。

比如hystrix.command.default.execution.isolation.thread.timeoutInMillisecond=3000,就將Hystrix 的默認超時時間改成了3s。這裏改的是 Hystrix的全局默認超時時間,某些時候,我們只需要對某些特定的服務進行超時時間自定義,只需要使用服務在 Eureka 中註冊的instanceId替換上面配置項中的 default即可。比如hystrix.command.order.execution.isolation.thread.timeoutInMillisecond=3000便是將 order 服務的 Hystrix 超時時間修改成了3s,其他服務還是默認的1s。

上面的超時時間是 Hystrix 的默認熔斷時間(熔斷這個概念我們會在後面專門講述),Ribbon的調用同樣存在超時可能。Ribbon 的調用超時默認爲5s(對於微服務來說,5s 其實已經很長了。所以這個配置項不建議大家修改。)。可以使用 serviceId.ribbon.ReadTimeout=10000來配置網關的 Ribbon 超時時間爲10s。

6.3 過濾器

網關作爲所有的服務的入口必經服務,在提供路由基本路由功能的前提下,更重要的一個功能是可以很方便地編寫針對所有流經網關服務的自定義邏輯,這個時候,纔是網關發揮真正威力的時候。一般來說,這種自定義邏輯類似於一個“橫切面”,可以方便地對安全性、日誌進行控制,甚至於對所有服務進行跟蹤。

在Zuul中一般使用過濾器實現這種自定義邏輯。

Zuul支持以下四種類型的過濾器。

  • pre過濾器:在Zuul將請求實際發送到目的微服務之前調用。前置過濾器多用來進行安全控制。
  • post過濾器:在目標微服務被調用後還沒將響應返回客戶端時調用。後置過濾器一般用來處理返回數據,可以對數據進行一些特定修改。
  • route過濾器:在目標微服務被調用前調用。一般用來實現自定義的路由策略,可以用來實現動態路由,實現灰度路由等功能。
  • error過濾器:在處理請求發送錯誤時被調用。

我們常用主要是pre、post和route三種過濾器。

通過下面一段代碼可以看到如何定義不同類型的過濾器:

package cn.com.hanbinit.gateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

@Component
public class CustomFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return PRE_DECORATION_FILTER_ORDER - 1;
    }

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

    @Override
    public Object run() throws ZuulException {
        System.out.println("preFilter...");
        return null;
    }
}

在Zuul中創建filter,一定要先繼承ZuulFilter類,並實現上面代碼中的四個方法。不同的類型主要取決於filterType的返回值。可以通過查看com.netflix.zuul.ZuulFilter中的filterType方法註釋來了解具體的支持類型。

to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering, "route" for routing to an origin, "post" for post-routing filters, "error" for error handling. We also support a "static" type for static responses see StaticResponseFilter.Any filterType made be created or added and run by calling FilterProcessor.runFilters(type) @return A String representing that type.

通過註釋可以看到,這裏通過返回pre,route,post,以及和static來分別指定不同的攔截器類型。

shouldFilter方法根據返回值來確認當前的攔截器是否生效,這裏可以通過外部的配置項來指定,也可以加入一些特殊的業務邏輯來判斷是否要啓用當前攔截器。

filterOrder返回int數字,標識當前攔截器的優先級,數字越小,級別越高。

最後的run方法中要實現的便是攔截器的自定義邏輯了。開動大腦,這裏可以乾的事情太多太多。

6.4 小結

本節簡單介紹了Spring Cloud Zuul,作爲微服務系統的網關,Zuul通過一些列的攔截器可以完成很多的工作。新版本的Spring Cloud中推薦使用的Spring Cloud Gateway 後面再單獨聊。

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