Spring Cloud (七):API 閘道器
在微服務架構中,不同服務有不同的地址,客戶端需要和多個服務互動,這會帶來一些問題
- 客戶端需要多次請求不同的微服務,增加複雜性
- 存在跨域請求
- 每個微服務都有獨立認證
- 微服務重構時,客戶端也要跟著改
API 閘道器就是客戶端和服務端的中間層,所有請求都會先經過 API 閘道器,再由 API 閘道器發給後端的微服務
使用了 API 閘道器後可以做統一的登入、認證、監控、日誌、限流、負載均衡、轉發,簡化了前後端的互動和開發
API 閘道器有 nginx,kong,zuul,Spring Cloud Gateway,F5 等
zuul
zuul 是 Netflix 開源的微服務閘道器元件,它可以和 Eureka、Robbin、Hystrix 等元件配合使用
zuul 的功能
- 認證和安全
- 預警和監控
- 動態路由
- 壓力測試
- 負載均衡
- 靜態處理
- 多區域彈性
啟動 zuul 的程式碼如下
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
@SpringBootApplication @EnableZuulProxy public class ZuulServerApplication { public static void main(String[] args) { SpringApplication.run(ZuulServerApplication.class, args); } }
server: port: 9000 zuul: prefix: /api # 訪問閘道器路徑的字首 routes: service-01: # 服務的名字,可以是任意名字,不會出現在 zuul 的 url 中 path: /product-service/** # 將服務的這個 path,對映到下面的 URL,或是 service id (如果使用了 Consul 或 Eureka) url: http://localhost:8501 # 比如 zuul-host:zuul-port/api/product-service/product/1 會被對映成 http://localhost:8501/product/1 # service-id: consul-service-1 service-02: path: /hello-service/** url: http://localhost:8505 # service-id: consul-service-1 retryable: true
訪問 http://localhost:9000/api/product-service/product/1
實際上會被轉發到 http://localhost:8501/product/1
Spring Cloud Gateway
zuul 有版本 1 和版本 2,spring cloud 整合的是版本 1,後續可能不會再改,而是推出 Spring Cloud Gateway
Spring Cloud Gateway 是基於 WebFlux 框架實現的,而 WebFlux 框架底層則使用了高效能的 Reactor 模式通訊框架 Netty
Spring Cloud Gateway 的特性 (https://github.com/spring-cloud/spring-cloud-gateway)
- 基於Spring 5, Spring Boot 2 and Project Reactor
- 動態路由
- 基於 HTTP 請求的任意屬性(Path, Method, Header, Host, etc…)做路由匹配
- Filters 作用於匹配路由
- Filters 可以修改 HTTP 請求和響應
- 支援 Spring Cloud DiscoveryClient
- 支援負載均衡、限流熔斷等功能
spring cloud gateway 的匹配機制比較強大,啟動 spring cloud gateway 的程式碼如下
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
注意需要移除 spring-boot-starter-web,並且如果有一些 dependency 間接依賴 spring-boot-starter-web,那麼需要通過 exclude 排除掉,不然會報錯:Consider defining a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' in your configuration.
如果用到了 hystrix 的容錯功能,那麼還要把 hystrix 的依賴新增上
@SpringBootApplication
public class SpringCloudGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudGatewayApplication.class, args);
}
}
server:
port: 9000
spring:
cloud:
gateway:
routes:
- id: service-01 ## 每個路由的 id,任意名字,不會出現在 url 中
uri: http://localhost:8501 ## 和這個路由匹配的請求都轉發到 http://localhost:8501
order: 0 ## 優先順序,一個請求有可能和多個路由匹配,會執行高優先順序的路由
predicates: ## 謂詞,滿足下面列舉的條件的請求,就和這個路由匹配上
- Path=/product-service/** ## 匹配 url 為 /product-service/** 的請求
## 即 gateway-host:gateway-port/product-service/** 會被轉發到 http://localhost:8501/**
filters: ## 過濾,對請求做一些處理(下面這裡是兩個過濾器)
- StripPrefix=1 ## 第一個過濾器:截斷路徑中的第一個欄位,比如 /test/abc 變成 /abc
- name: Hystrix ## 第二個過濾器:通過 Hystrix 做熔斷
args:
name: fallbackCmdA
fallbackUri: forward:/fallbackA ## 將 forward 服務的 fallbackA 路徑的輸出作為熔斷的返回
hystrix:
command:
fallbackCmdA:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000
也可以通過程式碼實現這些配置功能
spring cloud gateway 的配置功能很強,下面再舉一些例子
# 請求中包含 id 引數的即可匹配路由,比如
# localhost:9000?id=2
predicates:
- Query=id
# 請求中包含 id 引數,且 id 引數以 abc 開頭的即可匹配路由,比如
# localhost:9000?id=abc123
# 注意這裡的 abc. 是正則表示式
predicates:
- Query=id, abc.
# 請求的 header 中包含 X-Request-Id 引數,且引數的值是數字,即可匹配路由,比如
# curl -H "X-Request-Id:88" localhost:9000
predicates:
- Header=X-Request-Id, \d+
# 請求的 cookie 中包含 sessionId,且值為 test,即可匹配路由,比如
# curl --cookie "sessionId=test" localhost:9000
predicates:
- Cookie=sessionId, test
# GET 請求即可匹配路由
predicates:
- Method=GET
# 請求的路徑滿足要求,即可匹配路由,比如
# curl localhost:9000/foo/1
# curl localhost:9000/foo/test
predicates:
- Path=/foo/{segment}
# 請求的遠端地址滿足要求即可匹配路由
predicates:
- RemoteAddr=192.168.1.1/24
# 可以組合使用
predicates:
- Path=/product-service
- Method=GET
- Query=id
Spring Cloud Gateway 的功能配置比 zuul 要多
nginx
nginx 的作用
- 負載均衡
- 地址對映和埠對映
- 靜態檔案支援、偽靜態
- 快取靜態和偽靜態頁面
- 緩衝請求和響應
- 高可用性,高併發抗壓能力,都比較強
- 訪問控制、限速等功能
安裝 Nginx
sudo apt-get install nginx
Nginx 配置
/etc/nginx/nginx.conf
這個配置檔案會引入下面兩個目錄的配置檔案
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
建立 /etc/nginx/conf.d/test.conf (配置檔案可以有多個)
server {
listen 80;
server_name localhost;
# location 的 URL 匹配語法
# ~* 表示正則表示式,不區分大小寫
# ~ 表示正則表示式,要區分大小寫
# = 表示精確匹配
# 沒有修飾符的,以指定模式開始,比如 location / 匹配所有以 / 開始的 URL
# 靜態頁面,直接讀取 html 檔案
location ~* .*\.html$ {
gzip on;
root /usr/share/nginx/html;
}
# 動態頁面,轉發給後端
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Nginx 效能強,穩定性高,適合作為全域性的閘道器,但有的功能不具備,比如熔斷等
kong
kong 是一個基於 nginx 的,用 nginx_lua 模組編寫擴充套件外掛的閘道器
kong 可以使用 Cassandra/PostgreSQL 資料庫來儲存資料
https://docs.konghq.com/install/ubuntu/
$ sudo apt-get update
$ sudo apt-get install -y apt-transport-https curl lsb-core
$ echo "deb https://kong.bintray.com/kong-deb `lsb_release -sc` main" | sudo tee -a /etc/apt/sources.list
$ curl -o bintray.key https://bintray.com/user/downloadSubjectPublicKey?username=bintray
$ sudo apt-key add bintray.key
$ sudo apt-get update
$ sudo apt-get install -y kong
配置檔案
cp /etc/kong/kong.conf.default /etc/kong/kong.conf
如果配置資料庫
CREATE USER kong;
CREATE DATABASE kong OWNER kong;
kong migrations bootstrap [-c /etc/kong/kong.conf]
如果不配置資料庫
## 會在當前路徑生成 kong.yml
kong config init
## 在 kong.conf 檔案裡把資料庫選項關掉
database = off
declarative_config = /path/to/kong.yml
啟動 kong
kong start [-c /etc/kong/kong.conf] [--nginx-conf /path/to/custom_nginx.template]
kong 預設監聽以下埠
- 8000:監聽客戶端的 HTTP 請求,轉發到後端服務
- 8443:監聽客戶端的 HTTPS 請求,轉發到後端服務,可以 disable
- 8001:監聽 Admin API HTTP 請求,用於配置 Kong
- 8444:監聽 Admin API HTTPS 請求,用於配置 Kong
可以通過向 8001 埠傳送 POST 請求進行配置
過載 kong
kong reload
停止 kong
kong stop
kong 可以通過 --nginx-conf 指定 nginx 配置
F5
F5 是直接通過硬體實現,和系統及應用無關,只從網路層判斷處理,效能強,缺點是成本高,配置可能比其他的複雜