Spring Cloud 入門 之 Zuul 篇(五)

原文地址:Spring Cloud 入門 之 Zuul 篇(五)
博客地址:http://www.extlight.com前端

1、前言

隨着業務的擴展,微服務會不對增長,相應的其對外開放的 API 接口也勢必增多,這不利於前端的調用以及不一樣場景下數據的返回,所以,咱們一般都須要設計一個 API 網關做爲一個統一的 API 入口,來組合一個或多個內部 API。java

2、簡單介紹

2.1 API 網關使用場景

黑白名單: 實現經過 IP 地址控制請求的訪問

日誌:實現訪問日誌的記錄,進而實現日誌分析,處理性能指標等

協議適配:實現通訊協議的校驗、適配轉換的功能

身份認證:對請求進行身份認證

計流限流:能夠設計限流規則,記錄訪問流量

路由:將請求進行內部(服務)轉發

2.2 API 網關的實現

業界經常使用的 API 網關有不少方式,如:Spring Cloud Zuul、 Nginx、Tyk、Kong。本篇介紹的對象正是 Spring Cloud Zuulgit

Zuul 是 Netflix 公司開源的一個 API 網關組件,提供了認證、鑑權、限流、動態路由、監控、彈性、安全、負載均衡、協助單點壓測等邊緣服務的框架。github

Spring Cloud Zuul 是基於 Netflix Zuul 的微服務路由和過濾器的解決方案,也用於實現 API 網關。其中,路由功能負責將外部請求轉發到具體的微服務實例上,是實現外部訪問統一入門的基礎。而過濾功能是負責對請求的處理過程進行干預,是實現請求校驗、服務聚合等功能的基礎。web

Spring Cloud Zuul 和 Eureka 進行整合時,Zuul 將自身註冊到 Eureka 服務中,同時從 Eureka 中獲取其餘微服務信息,以便請求能夠準確的經過 Zuul 轉發到具體微服務上。spring

3、實戰演練

本次測試案例基於以前發表的文章中介紹的案例進行演示,不清楚的讀者請先轉移至 《Spring Cloud 入門 之 Hystrix 篇(四)》 進行瀏覽。數據庫

當前的項目列表以下:api

服務實例 端口 描述
common-api - 公用的 api,如:實體類
eureka-server 9000 註冊中心(Eureka 服務端)
goods-server 8081 商品服務(Eureka 客戶端)
goods-server-02 8082 商品服務(Eureka 客戶端)
goods-server-03 8083 商品服務(Eureka 客戶端)
order-server 8100 訂單服務(Eureka 客戶端)

建立一個爲名 gateway-server 的 Spring Boot 項目。緩存

3.1 添加依賴

<!-- eureka 客戶端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

<!-- zuul 網關 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

3.2 配置文件

server:
    port: 9600
 
spring:
    application:
        name: gateway

eureka:
    instance:
        instance-id: gateway-9600
        prefer-ip-address: true 
    client:
        service-url:
            defaultZone: http://localhost:9000/eureka/  # 註冊中心訪問地址

3.3 啓動 Zuul

在啓動類上添加 @EnableZuulProxy 註解:安全

@EnableZuulProxy
@SpringBootApplication
public class GatewayApplication {

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

啓動上邊的全部項目,打開 Postman 請求訂單下單接口,以下圖:

圖中,咱們首先不通過網關直接訪問 order-server 項目請求地址:http://localhost:8100/order/place

以後再修改爲訪問 gateway-server 項目的請求地址:http://localhost:9600/order/order/place

最終,響應結果都同樣。

提示:http://localhost:9600/order/order/place 中第一個 order 表示的是註冊在 Eureka 上的訂單服務名稱。

3.4 zuul 經常使用配置

修改路由:

zuul:
  sensitive-headers: # 全局忽略敏感頭,即容許接收 cookie 等請求頭信息   
  routes:
    extlight: # 任意名字,保證惟一便可
      path: /extlight/** # 自定義,真正用到的請求地址
      service-id: ORDER  # 路由到的目標服務名稱

將訂單服務的路由名稱改爲 extlight。

使用 Postman 請求下單接口,運行結果:

請求成功。

禁用路由:

zuul:
  ignored-patterns:
  - /order/order/**

http://localhost:9600/order/order/place 沒法被正常路由到訂單服務,響應返回 404。

路由加前綴:

zuul:
  prefix: /api

全部請求中的 path 須要添加 api 前綴。如: http://localhost:9600/extlight/order/place 須要改爲 http://localhost:9600/api/extlight/order/place

設置敏感頭:

zuul:
  sensitive-headers: # 設置全局敏感頭,若是爲空,表示接收全部敏感頭信息

zuul:
  routes:
    extlight: # 任意名字,保證惟一便可
      path: /extlight/** # 自定義,真正用到的請求地址
      service-id: ORDER  # 路由到的目標服務名稱
      sensitive-headers: # 針對 /extlight/ 的請求設置敏感頭信息

4、Zuul 自定義過濾器

Zuul 的核心技術就是過濾器,該框架提供了 ZuulFilter 接口讓開發者能夠自定義過濾規則。

咱們以身份檢驗爲例,自定義 ZuulFilter 過濾器實現該功能。

4.1 建立用戶服務

新建名爲 user-server 的項目。

添加依賴:

<!-- common api -->
<dependency>
  <groupId>com.extlight.springcloud</groupId>
  <artifactId>common-api</artifactId>
  <version>${parent-version}</version>
</dependency>
    
<!-- springmvc -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- eureka 客戶端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

application.yml:

server:
    port: 8200

spring:
  application:
    name: USER
    
eureka:
    instance:
        instance-id: user-api-8200
        prefer-ip-address: true # 訪問路徑能夠顯示 IP
    client:
        service-url:
            defaultZone: http://localhost:9000/eureka/  # 註冊中心訪問地址

登陸接口:

@RestController
@RequestMapping("/user")
public class LoginController {

    @PostMapping("/login")
    public Result login(String username, String password, HttpServletResponse response) {
        
        
        if ("admin".equals(username) && "admin".equals(password)) {
            // 模擬生成 token,實際開發中 token 應存放在數據庫或緩存中
            String token = "123456";
            Cookie cookie = new Cookie("token", token);
            cookie.setPath("/");
            cookie.setMaxAge(60 * 10);
            response.addCookie(cookie);
            
            return Result.success();
        }
        
        return Result.fail(401, "帳號或密碼錯誤");
    }
}

user-server 啓動類:

@EnableEurekaClient
@SpringBootApplication
public class UserServerApplication {

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

4.2 建立 ZuulFilter 過濾器

在 gateway-server 項目中,新建一個過濾器,須要繼承 ZuulFilter 類

@Component
public class AuthenticationFilter extends ZuulFilter {

    /**
     * 是否開啓過濾
     */
    @Override
    public boolean shouldFilter() {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        
        boolean flag = request.getRequestURI().contains("/login");
        // 若是是登陸請求不進行過濾
        if (flag) {
            System.out.println("========不執行 zuul 過濾方法=======");
        } else {
            System.out.println("========執行 zuul 過濾方法=======");
        }
        return !flag;
    }

    /**
     * 過濾器執行內容
     */
    @Override
    public Object run() throws ZuulException {
        
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String token = request.getParameter("token");
        // 此處模擬獲取數據庫或緩存中的 token
        String dbToken = "123456";
        // 此處簡單檢驗 token
        if (token == null || "".equals(token) || !dbToken.equals(token)) {
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        
        return null;
    }

    /**
     * 過濾器類型
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 過濾器執行順序
     */
    @Override
    public int filterOrder() {
        return 0;
    }

}

其中,filterType 有 4 種類型:

pre: 這種過濾器在請求被路由以前調用。咱們可利用這種過濾器實現身份驗證、在集羣中選擇請求的微服務、記錄調試信息等。

routing:這種過濾器將請求路由到微服務。這種過濾器用於構建發送給微服務的請求,並使用 Apache HttpClient 或 Netfilx Ribbon 請求微服務。

post:這種過濾器在路由到微服務之後執行。這種過濾器可用來爲響應添加標準的 HTTP Header、收集統計信息和指標、將響應從微服務發送給客戶端等。

error:在其餘階段發生錯誤時執行該過濾器。

其過濾順序以下圖:

4.3 測試過濾器

運行全部項目,測試操做步驟以下:

請求用戶服務的登陸接口(http://localhost:9600/user/user/login),請求不執行 zuul 過濾方法,而且請求響應返回的 cookie 包含 token

請求訂單服務的下單接口(http://localhost:9600/extlight/order/place),但不攜帶 token,請求須要執行 zuul 過濾方法,請求響應 401 權限不足

請求訂單服務的下單接口(http://localhost:9600/extlight/order/place),攜帶以前登陸接口返回的 token,請求須要執行 zuul 過濾方法,校驗經過後路由到訂單服務執行以後的操做

測試效果圖以下:

5、案例源碼

Zuul demo 源碼

6、參考資料

Announcing Zuul: Edge Service in the Cloud

相關文章
相關標籤/搜索