Spring Cloud 系列之 Netflix Zuul 服務網關(一)

什麼是 Zuul

Zuul 是從設備和網站到應用程序後端的所有請求的前門。作爲邊緣服務應用程序,Zuul 旨在實現動態路由,監視,彈性和安全性。Zuul 包含了對請求的路由過濾兩個最主要的功能。

Zuul 是 Netflix 開源的微服務網關,它可以和 Eureka、Ribbon、Hystrix 等組件配合使用。Zuul 的核心是一系列的過濾器,這些過濾器可以完成以下功能:

  • 身份認證與安全:識別每個資源的驗證要求,並拒絕那些與要求不符的請求
  • 審查與監控:在邊緣位置追蹤有意義的數據和統計結果,從而帶來精確的生產試圖
  • 動態路由:動態地將請求路由到不同的後端集羣
  • 壓力測試:逐漸增加只想集羣的流量,以瞭解性能
  • 負載分配:爲每一種負載類型分配對應容量,並棄用超出限定值的請求
  • 靜態響應處理:在邊緣位置直接建立部份響應,從而避免其轉發到內部集羣\
  • 多區域彈性:跨越AWS Region進行請求路由,旨在實現ELB(Elastic Load Balancing)使用的多樣化,以及讓系統的邊緣更貼近系統的使用者

什麼是服務網關

API Gateway(APIGW / API 網關),顧名思義,是出現在系統邊界上的一個面向 API 的、串行集中式的強管控服務,這裏的邊界是企業 IT 系統的邊界,可以理解爲企業級應用防火牆,主要起到隔離外部訪問與內部系統的作用。在微服務概念的流行之前,API 網關就已經誕生了,例如銀行、證券等領域常見的前置機系統,它也是解決訪問認證、報文轉換、訪問統計等問題的。

API 網關的流行,源於近幾年來移動應用與企業間互聯需求的興起。移動應用、企業互聯,使得後臺服務支持的對象,從以前單一的 Web 應用,擴展到多種使用場景,且每種使用場景對後臺服務的要求都不盡相同。這不僅增加了後臺服務的響應量,還增加了後臺服務的複雜性。隨着微服務架構概念的提出,API 網關成爲了微服務架構的一個標配組件

API 網關是一個服務器,是系統對外的唯一入口。API 網關封裝了系統內部架構,爲每個客戶端提供定製的 API。所有的客戶端和消費端都通過統一的網關接入微服務,在網關層處理所有非業務功能。API 網關並不是微服務場景中必須的組件,如下圖,不管有沒有 API 網關,後端微服務都可以通過 API 很好地支持客戶端的訪問。

但對於服務數量衆多、複雜度比較高、規模比較大的業務來說,引入 API 網關也有一系列的好處:

  • 聚合接口使得服務對調用者透明,客戶端與後端的耦合度降低
  • 聚合後臺服務,節省流量,提高性能,提升用戶體驗
  • 提供安全、流控、過濾、緩存、計費、監控等 API 管理功能

爲什麼要使用網關

  • 單體應用:瀏覽器發起請求到單體應用所在的機器,應用從數據庫查詢數據原路返回給瀏覽器,對於單體應用來說是不需要網關的。
  • 微服務:微服務的應用可能部署在不同機房,不同地區,不同域名下。此時客戶端(瀏覽器/手機/軟件工具)想要請求對應的服務,都需要知道機器的具體 IP 或者域名 URL,當微服務實例衆多時,這是非常難以記憶的,對於客戶端來說也太複雜難以維護。此時就有了網關,客戶端相關的請求直接發送到網關,由網關根據請求標識解析判斷出具體的微服務地址,再把請求轉發到微服務實例。這其中的記憶功能就全部交由網關來操作了。

總結

如果讓客戶端直接與各個微服務交互:

  • 客戶端會多次請求不同的微服務,增加了客戶端的複雜性
  • 存在跨域請求,在一定場景下處理相對複雜
  • 身份認證問題,每個微服務需要獨立身份認證
  • 難以重構,隨着項目的迭代,可能需要重新劃分微服務
  • 某些微服務可能使用了防火牆/瀏覽器不友好的協議,直接訪問會有一定的困難

因此,我們需要網關介於客戶端與服務器之間的中間層,所有外部請求率先經過微服務網關,客戶端只需要與網關交互,只需要知道網關地址即可。這樣便簡化了開發且有以下優點:

  • 易於監控,可在微服務網關收集監控數據並將其推送到外部系統進行分析
  • 易於認證,可在微服務網關上進行認證,然後再將請求轉發到後端的微服務,從而無需在每個微服務中進行認證
  • 減少了客戶端與各個微服務之間的交互次數

網關解決了什麼問題

網關具有身份認證與安全、審查與監控、動態路由、負載均衡、緩存、請求分片與管理、靜態響應處理等功能。當然最主要的職責還是與“外界聯繫”。

總結一下,網關應當具備以下功能:

  • 性能:API 高可用,負載均衡,容錯機制。
  • 安全:權限身份認證、脫敏,流量清洗,後端簽名(保證全鏈路可信調用),黑名單(非法調用的限制)。
  • 日誌:日誌記錄,一旦涉及分佈式,全鏈路跟蹤必不可少。
  • 緩存:數據緩存。
  • 監控:記錄請求響應數據,API 耗時分析,性能監控。
  • 限流:流量控制,錯峯流控,可以定義多種限流規則。
  • 灰度:線上灰度部署,可以減小風險。
  • 路由:動態路由規則。

常用網關解決方案

Nginx + Lua

Nginx 是由 IgorSysoev 爲俄羅斯訪問量第二的 Rambler.ru 站點開發的,一個高性能的 HTTP 和反向代理服務器。Ngnix 一方面可以做反向代理,另外一方面做可以做靜態資源服務器。

  • Nginx 是 C 語言開發,而 Zuul 是 Java 語言開發
  • Nginx 負載均衡實現,採用服務器實現負載均衡,而 Zuul 負載均衡的實現是採用 Ribbon + Eureka 來實現本地負載均衡
  • Nginx 適合於服務器端負載均衡,Zuul 適合微服務中實現網關
  • Nginx 相比 Zuul 功能會更加強大,因爲 Nginx 可以整合一些腳本語言(Nginx + Lua)
  • Nginx 是一個高性能的 HTTP 和反向代理服務器,也是一個 IMAP / POP3 /SMIP 服務器。Zuul 是 Spring Cloud Netflix 中的開源的一個 API Gateway 服務器,本質上是一個 Servlet 應用,提供動態路由,監控,彈性,安全等邊緣服務的框架。 Zuul 相當於是從設備和網站到應用程序後端的所有請求的前門。
  • Nginx 適合做門戶網關,是作爲整個全局的網關,對外的處於最外層的那種;而 Zuul 屬於業務網關,主要用來對應不同的客戶端提供服務,用於聚合業務。各個微服務獨立部署,職責單一,對外提供服務的時候需要有一個東西把業務聚合起來。
  • Zuul 可以實現熔斷、重試等功能,這是 Nginx 不具備的。

Kong

Kong 是 Mashape 提供的一款 API 管理軟件,它本身是基於 Ngnix + Lua 的,但比 Nginx 提供了更簡單的配置方式,數據採用了 ApacheCassandra/PostgreSQL 存儲,並且提供了一些優秀的插件,比如驗證,日誌,調用頻次限制等。Kong 非常誘人的地方就是提供了大量的插件來擴展應用,通過設置不同的插件可以爲服務提供各種增強的功能。

優點:基於 Nginx 所以在性能和穩定性上都沒有問題。Kong 作爲一款商業軟件,在 Nginx 上做了很擴展工作,而且還有很多付費的商業插件。Kong 本身也有付費的企業版,其中包括技術支持、使用培訓服務以及 API 分析插件。

缺點:如果你使用 Spring Cloud,Kong 如何結合目前已有的服務治理體系?

Traefik

Traefik 是一個開源的 GO 語言開發的爲了讓部署微服務更加便捷而誕生的現代HTTP反向代理、負載均衡工具。 它支持多種後臺 (Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd, Zookeeper, BoltDB, Rest API, file…) 來自動化、動態的應用它的配置文件設置。Traefik 擁有一個基於 AngularJS 編寫的簡單網站界面,支持 Rest API,配置文件熱更新,無需重啓進程。高可用集羣模式等。

相對 Spring Cloud 和 Kubernetes 而言,目前比較適合 Kubernetes。

Spring Cloud Netflix Zuul

Zuul 是 Netflix 公司開源的一個 API 網關組件,Spring Cloud 對其進行二次基於 Spring Boot 的註解式封裝做到開箱即用。目前來說,結合 Sring Cloud 提供的服務治理體系,可以做到請求轉發,根據配置或者默認的路由規則進行路由和 Load Balance,無縫集成 Hystrix。

雖然可以通過自定義 Filter 實現我們想要的功能,但是由於 Zuul 本身的設計是基於單線程的接收請求和轉發處理,是阻塞 IO,不支持長連接。目前來看 Zuul 就顯得很雞肋,隨着 Zuul 2.x 一直跳票(2019 年 5 月發佈了 Zuul 2.0 版本),Spring Cloud 推出自己的 Spring Cloud Gateway。

大意就是:Zuul 已死,Spring Cloud Gateway 永生(手動狗頭)。

Zuul 1.0

Zuul 2.0

Spring Cloud Gateway

Spring Cloud Gateway 作爲 Spring Cloud 生態系統中的網關,目標是替代 Netflix Zuul,其不僅提供統一的路由方式,並且還基於 Filter 鏈的方式提供了網關基本的功能。目前最新版 Spring Cloud 中引用的還是 Zuul 1.x 版本,而這個版本是基於過濾器的,是阻塞 IO,不支持長連接。

Zuul 2.x 版本一直跳票,2019 年 5 月,Netflix 終於開源了支持異步調用模式的 Zuul 2.0 版本,真可謂千呼萬喚始出來。但是 Spring Cloud 已經不再集成 Zuul 2.x 了。

Spring Cloud Gateway 是基於 Spring 生態系統之上構建的 API 網關,包括:Spring 5,Spring Boot 2 和 Project Reactor。Spring Cloud Gateway 旨在提供一種簡單而有效的方法來路由到 API,併爲它們提供跨領域的關注點,例如:安全性,監視/指標,限流等。由於 Spring 5.0 支持 Netty,Http2,而 Spring Boot 2.0 支持 Spring 5.0,因此 Spring Cloud Gateway 支持 Netty 和 Http2 順理成章。

總結

API 網關在微服務架構中的作用大概是這樣的:

我們目前所學習的網關關注的就是 Aggr API Gateway 這部分,在這裏做聚合服務的操作。

環境準備

zuul-demo 聚合工程。SpringBoot 2.2.4.RELEASESpring Cloud Hoxton.SR1

  • eureka-server:註冊中心
  • eureka-server02:註冊中心
  • product-service:商品服務,提供了根據主鍵查詢商品接口 http://localhost:7070/product/{id}
  • order-service:訂單服務,提供了根據主鍵查詢訂單接口 http://localhost:9090/order/{id} 且訂單服務調用商品服務。

Nginx 實現 API 網關

點擊鏈接觀看:Nginx 實現 API 網關視頻(獲取更多請關注公衆號「哈嘍沃德先生」)

之前的課程中我們已經詳細的講解過 Nginx 關於反向代理、負載均衡等功能的使用,這裏不再贅述。這裏主要通過 Nginx 來實現 API 網關方便大家更好的學習和理解 Zuul 的使用。

下載

官網:http://nginx.org/en/download.html 下載穩定版。爲了方便學習,請下載 Windows 版本。

安裝

解壓文件後直接運行根路徑下的 nginx.exe 文件即可。

Nginx 默認端口爲 80,訪問:http://localhost:80/ 看到下圖說明安裝成功。

配置路由規則

進入 Nginx 的 conf 目錄,打開 nginx.conf 文件,配置路由規則:

http {

	...

    server {
        listen       80;
        server_name  localhost;

        ...

        # 路由到商品服務
        location /api-product {
            proxy_pass http://localhost:7070/;
        }

        # 路由到訂單服務
        location /api-order {
            proxy_pass http://localhost:9090/;
        }

        ...
    }
    
    ...
    
}

訪問

之前我們如果要訪問服務,必須由客戶端指定具體服務地址訪問,現在統一訪問 Nginx,由 Nginx 實現網關功能將請求路由至具體的服務。

訪問:http://localhost/api-product/product/1 結果如下:

訪問:http://localhost/api-order/order/1 結果如下:

Zuul 實現 API 網關

點擊鏈接觀看:Zuul 實現 API 網關視頻(獲取更多請關注公衆號「哈嘍沃德先生」)

官網文檔: https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.2.1.RELEASE/reference/html/#router-and-filter-zuul

搭建網關服務

創建項目

創建 zuul-server 項目。

添加依賴

添加 spring cloud netflix zuul 依賴。

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>zuul-server</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- 繼承父依賴 -->
    <parent>
        <groupId>com.example</groupId>
        <artifactId>zuul-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <!-- 項目依賴 -->
    <dependencies>
        <!-- spring cloud netflix zuul 依賴 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
    </dependencies>

</project>

配置文件

application.yml

server:
  port: 9000 # 端口

spring:
  application:
    name: zuul-server # 應用名稱

啓動類

啓動類需要開啓 @EnableZuulProxy 註解。

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
// 開啓 Zuul 註解
@EnableZuulProxy
public class ZuulServerApplication {

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

}

配置路由規則

URL 地址路由

# 路由規則
zuul:
  routes:
    product-service:              # 路由 id 自定義
      path: /product-service/**   # 配置請求 url 的映射路徑
      url: http://localhost:7070/ # 映射路徑對應的微服務地址

通配符含義:

通配符 含義 舉例 解釋
? 匹配任意單個字符 /product-service/? /product-service/a,/product-service/b,…
* 匹配任意數量字符不包括子路徑 /product-service/* /product-service/aa,/product-service/bbb,…
** 匹配任意數量字符包括所有下級路徑 /product-service/** /product-service/aa,/product-service/aaa/b/ccc

訪問:http://localhost:9000/product-service/product/1 結果如下:

服務名稱路由

微服務一般是由幾十、上百個服務組成,對於 URL 地址路由的方式,如果對每個服務實例手動指定一個唯一訪問地址,這樣做顯然是不合理的。

Zuul 支持與 Eureka 整合開發,根據 serviceId 自動從註冊中心獲取服務地址並轉發請求,這樣做的好處不僅可以通過單個端點來訪問應用的所有服務,而且在添加或移除服務實例時不用修改 Zuul 的路由配置。

添加 Eureka Client 依賴

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

配置註冊中心和路由規則

# 路由規則
zuul:
  routes:
    product-service:              # 路由 id 自定義
      path: /product-service/**   # 配置請求 url 的映射路徑
      serviceId: product-service  # 根據 serviceId 自動從註冊中心獲取服務地址並轉發請求

# 配置 Eureka Server 註冊中心
eureka:
  instance:
    prefer-ip-address: true       # 是否使用 ip 地址註冊
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port
  client:
    service-url:                  # 設置服務註冊中心地址
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/

啓動類

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
// 開啓 Zuul 註解
@EnableZuulProxy
// 開啓 EurekaClient 註解,目前版本如果配置了 Eureka 註冊中心,默認會開啓該註解
//@EnableEurekaClient
public class ZuulServerApplication {

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

}

訪問

訪問:http://localhost:9000/product-service/product/1 結果如下:

簡化路由配置

Zuul 爲了方便大家使用,提供了默認路由配置:路由 id 和 微服務名稱 一致,path 默認對應 /微服務名稱/**,所以以下配置就沒必要再寫了。

# 路由規則
zuul:
  routes:
    product-service:              # 路由 id 自定義
      path: /product-service/**   # 配置請求 url 的映射路徑
      serviceId: product-service  # 根據 serviceId 自動從註冊中心獲取服務地址並轉發請求

訪問

此時我們並沒有配置任何訂單服務的路由規則,訪問:http://localhost:9000/order-service/order/1 結果如下:

路由排除

我們可以通過路由排除設置不允許被訪問的資源。允許被訪問的資源可以通過路由規則進行設置。

URL 地址排除

# 路由規則
zuul:
  ignored-patterns: /**/order/**  # URL 地址排除,排除所有包含 /order/ 的路徑
  # 不受路由排除影響
  routes:
    product-service:              # 路由 id 自定義
      path: /product-service/**   # 配置請求 url 的映射路徑
      serviceId: product-service  # 根據 serviceId 自動從註冊中心獲取服務地址並轉發請求

服務名稱排除

# 路由規則
zuul:
  ignored-services: order-service # 服務名稱排除,多個服務逗號分隔,'*' 排除所有
  # 不受路由排除影響
  routes:
    product-service:              # 路由 id 自定義
      path: /product-service/**   # 配置請求 url 的映射路徑
      serviceId: product-service  # 根據 serviceId 自動從註冊中心獲取服務地址並轉發請求

路由前綴

zuul:
  prefix: /api

訪問

訪問:http://localhost:9000/api/product-service/product/1 結果如下:

下一篇我們講解 Zuul 網關過濾器實現統一鑑權以及網關過濾器異常統一處理,記得關注噢~

本文采用 知識共享「署名-非商業性使用-禁止演繹 4.0 國際」許可協議

大家可以通過 分類 查看更多關於 Spring Cloud 的文章。


🤗 您的點贊轉發是對我最大的支持。

📢 掃碼關注 哈嘍沃德先生「文檔 + 視頻」每篇文章都配有專門視頻講解,學習更輕鬆噢 ~

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