代碼參考:
Gitee:[https://gitee.com/xn2001/cloudcode/tree/master/07-cloud-gateway](https://gitee.com/xn2001/cloudcode/tree/master/07-cloud-gateway)
GitHub:[https://github.com/lexinhu/cloudcode/tree/master/07-cloud-gateway](https://github.com/lexinhu/cloudcode/tree/master/07-cloud-gateway)
Spring Cloud Gateway 是 Spring Cloud 的一個全新項目流妻,該項目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等響應式編程和事件流技術開發(fā)的網(wǎng)關序愚,它旨在為微服務架構提供一種簡單有效的統(tǒng)一的 API 路由管理方式。
Gateway 網(wǎng)關是我們服務的守門神蛇耀,所有微服務的統(tǒng)一入口框喳。
網(wǎng)關的核心功能特性:
- 請求路由
- 權限控制
-
限流
權限控制:網(wǎng)關作為微服務入口,需要校驗用戶是是否有請求資格锨并,如果沒有則進行攔截露该。
路由和負載均衡:一切請求都必須先經(jīng)過 gateway,但網(wǎng)關不處理業(yè)務第煮,而是根據(jù)某種規(guī)則解幼,把請求轉發(fā)到某個微服務,這個過程叫做路由包警。當然路由的目標服務有多個時撵摆,還需要做負載均衡。
限流:當請求流量過高時害晦,在網(wǎng)關中按照下流的微服務能夠接受的速度來放行請求特铝,避免服務壓力過大。
在 SpringCloud 中網(wǎng)關的實現(xiàn)包括兩種:
- gateway
- zuul
Zuul 是基于 Servlet 實現(xiàn)壹瘟,屬于阻塞式編程鲫剿。而 Spring Cloud Gateway 則是基于 Spring5 中提供的WebFlux,屬于響應式編程的實現(xiàn)稻轨,具備更好的性能灵莲。
入門使用
1、創(chuàng)建 SpringBoot 工程 gateway殴俱,引入網(wǎng)關依賴
2政冻、編寫啟動類
3、編寫基礎配置和路由規(guī)則
4线欲、啟動網(wǎng)關服務進行測試
<!--網(wǎng)關-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos服務發(fā)現(xiàn)依賴-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
創(chuàng)建 application.yml 文件赠幕,內(nèi)容如下:
server:
port: 10010 # 網(wǎng)關端口
spring:
application:
name: gateway # 服務名稱
cloud:
nacos:
server-addr: localhost:8848 # nacos地址
gateway:
routes: # 網(wǎng)關路由配置
- id: user-service # 路由id,自定義询筏,只要唯一即可
# uri: http://127.0.0.1:8081 # 路由的目標地址 http就是固定地址
uri: lb://userservice # 路由的目標地址 lb就是負載均衡榕堰,后面跟服務名稱
predicates: # 路由斷言,也就是判斷請求是否符合路由規(guī)則的條件
- Path=/user/** # 這個是按照路徑匹配,只要以/user/開頭就符合要求
我們將符合Path
規(guī)則的一切請求逆屡,都代理到 uri
參數(shù)指定的地址圾旨。
上面的例子中,我們將 /user/**
開頭的請求魏蔗,代理到 lb://userservice
砍的,其中 lb 是負載均衡(LoadBalance),根據(jù)服務名拉取服務列表莺治,實現(xiàn)負載均衡廓鞠。
重啟網(wǎng)關,訪問 http://localhost:10010/user/1 時谣旁,符合 /user/**
規(guī)則床佳,請求轉發(fā)到 uri:http://userservice/user/1
多個 predicates 的話,要同時滿足規(guī)則榄审,下文有例子砌们。
流程圖
路由配置包括:
1、路由id:路由的唯一標示
2搁进、路由目標(uri):路由的目標地址浪感,http代表固定地址,lb代表根據(jù)服務名負載均衡
3饼问、路由斷言(predicates):判斷路由的規(guī)則
4影兽、路由過濾器(filters):對請求或響應做處理
斷言工廠
我們在配置文件中寫的斷言規(guī)則只是字符串,這些字符串會被 Predicate Factory 讀取并處理莱革,轉變?yōu)槁酚膳袛嗟臈l件赢笨。
例如Path=/user/**
是按照路徑匹配,這個規(guī)則是由
org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory
類來處理的驮吱,像這樣的斷言工廠在 Spring Cloud Gateway 還有十幾個
官方文檔:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories
一般的茧妒,我們只需要掌握 Path,加上官方文檔的例子左冬,就可以應對各種工作場景了桐筏。
predicates:
- Path=/order/**
- After=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] #2031年之后才能訪問
過濾器工廠
GatewayFilter 是網(wǎng)關中提供的一種過濾器,可以對進入網(wǎng)關的請求和微服務返回的響應做處理拇砰。
Spring提供了31種不同的路由過濾器工廠梅忌。
官方文檔:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
下面我們以 AddRequestHeader 為例:
需求:給所有進入 userservice 的請求添加一個請求頭:sign=xn2001.com is eternal
只需要修改 gateway 服務的 application.yml文件,添加路由過濾即可除破。
spring:
cloud:
gateway:
routes: # 網(wǎng)關路由配置
- id: user-service # 路由id牧氮,自定義,只要唯一即可
# uri: http://127.0.0.1:8081 # 路由的目標地址 http就是固定地址
uri: lb://userservice # 路由的目標地址 lb就是負載均衡瑰枫,后面跟服務名稱
predicates: # 路由斷言踱葛,也就是判斷請求是否符合路由規(guī)則的條件
- Path=/user/** # 這個是按照路徑匹配丹莲,只要以/user/開頭就符合要求
filters:
- AddRequestHeader=sign, xn2001.com is eternal # 添加請求頭
如何驗證,我們修改 userservice 中的一個接口
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id, @RequestHeader(value = "sign", required = false) String sign) {
log.warn(sign);
return userService.queryById(id);
}
重啟兩個服務尸诽,訪問:http://localhost:10010/user/1
可以看到控制臺打印出了這個請求頭
當然甥材,Gateway 也是有全局過濾器的,如果要對所有的路由都生效性含,則可以將過濾器工廠寫到 default-filters 下:
spring:
cloud:
gateway:
default-filters:
- AddRequestHeader=sign, xn2001.com is eternal # 添加請求頭
全局過濾器
上面介紹的過濾器工廠洲赵,網(wǎng)關提供了 31 種,但每一種過濾器的作用都是固定的商蕴。如果我們希望攔截請求叠萍,做自己的業(yè)務邏輯則沒辦法實現(xiàn)。
全局過濾器的作用也是處理一切進入網(wǎng)關的請求和微服務響應绪商,與 GatewayFilter 的作用一樣苛谷。區(qū)別在于 GlobalFilter 的邏輯可以寫代碼來自定義規(guī)則;而 GatewayFilter 通過配置定義部宿,處理邏輯是固定的。
需求:定義全局過濾器瓢湃,攔截請求理张,判斷請求的參數(shù)是否滿足下面條件
參數(shù)中是否有 authorization
authorization 參數(shù)值是否為 admin
如果同時滿足則放行,否則攔截绵患。
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
// 測試:http://localhost:10010/order/101?authorization=admin
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 獲取第一個 authorization 參數(shù)
String authorization = exchange.getRequest().getQueryParams().getFirst("authorization");
if ("admin".equals(authorization)){
// 放行
return chain.filter(exchange);
}
// 設置攔截狀態(tài)碼信息
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// 設置攔截
return exchange.getResponse().setComplete();
}
// 設置過濾器優(yōu)先級雾叭,值越低優(yōu)先級越高
// 也可以使用 @Order 注解
@Override
public int getOrder() {
return 0;
}
}
過濾器順序
請求進入網(wǎng)關會碰到三類過濾器:DefaultFilter、當前路由的過濾器落蝙、GlobalFilter织狐;
請求路由后,會將三者合并到一個過濾器鏈(集合)中筏勒,排序后依次執(zhí)行每個過濾器.
排序的規(guī)則是什么呢移迫?
- 每一個過濾器都必須指定一個 int 類型的 order 值,order 值越小管行,優(yōu)先級越高厨埋,執(zhí)行順序越靠前。
- GlobalFilter 通過實現(xiàn) Ordered 接口捐顷,或者使用 @Order 注解來指定 order 值荡陷,由我們自己指定。
- 路由過濾器和 defaultFilter 的 order 由 Spring 指定迅涮,默認是按照聲明順序從1遞增废赞。
- 當過濾器的 order 值一樣時,會按照 defaultFilter > 路由過濾器 > GlobalFilter 的順序執(zhí)行叮姑。
跨域問題
不了解跨域問題的同學可以百度了解一下唉地;在 Gateway 網(wǎng)關中解決跨域問題還是比較方便的。
spring:
cloud:
gateway:
globalcors: # 全局的跨域處理
add-to-simple-url-handler-mapping: true # 解決options請求被攔截問題
corsConfigurations:
'[/**]':
allowedOrigins: # 允許哪些網(wǎng)站的跨域請求 allowedOrigins: “*” 允許所有網(wǎng)站
- "http://localhost:8090"
allowedMethods: # 允許的跨域ajax的請求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允許在請求中攜帶的頭信息
allowCredentials: true # 是否允許攜帶cookie
maxAge: 360000 # 這次跨域檢測的有效期