SpringCloud Gateway網(wǎng)關(guān)組件,你真的懂了嗎?
點擊關(guān)注公眾號,Java干貨及時送達

Java技術(shù)迷 | 出品
對于傳統(tǒng)的單體應(yīng)用,我們似乎沒有遇到過某種問題,它在如今盛行的微服務(wù)架構(gòu)中非常常見,它就是接口訪問。在單體應(yīng)用中,我們訪問的都僅僅是這一個應(yīng)用的內(nèi)容,而微服務(wù)則不同,在微服務(wù)架構(gòu)中,一個應(yīng)用被拆分成了很多的微服務(wù):
這給前端訪問產(chǎn)生了一些麻煩,一般來說,前端都會抽取出一個公共的訪問地址,但這些微服務(wù)都分布在不同的機器上,導(dǎo)致訪問地址都是不一樣的,基于此,網(wǎng)關(guān)的出現(xiàn)能夠輕松解決這一問題。
所有想要訪問接口的客戶端、用戶等都先將請求發(fā)至網(wǎng)關(guān),再由網(wǎng)關(guān)來解決將該請求交給哪個服務(wù)進行處理。
SpringCloud為我們提供了網(wǎng)關(guān)的實現(xiàn)——Gateway,通過Gateway,我們能夠?qū)崿F(xiàn)身份認證和權(quán)限校驗;服務(wù)路由和負載均衡;請求限流等功能。
網(wǎng)關(guān)初體驗
SpringCloud Gateway是作為一個獨立的微服務(wù)工作的,所以我們需要創(chuàng)建一個SpringBoot應(yīng)用,并引入Nacos和Gateway的依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
Gateway也是需要注冊到Nacos中的,因為只有注冊到Nacos中網(wǎng)關(guān)才能夠發(fā)現(xiàn)有哪些服務(wù)是健康的,有哪些服務(wù)可以正常使用。在網(wǎng)關(guān)服務(wù)中,我們無需編寫任何代碼,只需要在application.yml中填寫相關(guān)配置即可:
server:
port: 10000
spring:
application:
name: service-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes: # 網(wǎng)關(guān)的路由配置
- id: service-user-route
uri: lb://service-user
predicates:
- Path:=/user/**
網(wǎng)關(guān)的路由配置總共需要配置三項,其中id是該路由的唯一標(biāo)識,uri指定的是需要路由到的服務(wù)地址,而predicates表示斷言,如果滿足斷言的要求,則網(wǎng)關(guān)便會將請求交給uri指定的服務(wù), lb:// 表示將請求負載均衡到 service-user 服務(wù)。斷言的形式有很多,比如這里使用的Path,它是用來判斷請求路徑的,當(dāng)請求路徑以user開頭,該請求就會被這一網(wǎng)關(guān)配置處理。
接下來在service-user服務(wù)中編寫一個方法:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/find/{userId}")
public User find(@PathVariable("userId") Long userId){
User user = userService.findById(userId);
return user;
}
}
將這兩個應(yīng)用分別啟動,然后訪問 http://localhost:10000/user/find/1,注意一定是訪問網(wǎng)關(guān)服務(wù),所以端口是10000,然后訪問路徑為 /user/find/1 ,由于路徑以user開頭,所以該請求就會被網(wǎng)關(guān)交給service-user服務(wù)進行處理。
路由配置
在剛剛的例子中,我們使用到了一個路徑的路由斷言,只需要在-Path中配置/user/**,那么以user開頭的請求就會被網(wǎng)關(guān)處理,這是如何實現(xiàn)的呢?事實上,Gateway中有很多的路由斷言工廠,當(dāng)我們在配置文件中對斷言進行配置后,這些配置就會被路由斷言工廠進行解析并處理,而-Path配置就是由org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory來處理的。SpringCloud Gateway中一共提供了11種基本的路由斷言工廠,分別如下:
1.BeforeRoutePredicateFactory:判斷是否為某個時間點之前的請求2.AfterRoutePredicateFactory:判斷是否為某個時間點之后的請求3.BetweenRoutePredicateFactory:判斷是否為某兩個時間點之間的請求4.CookieRoutePredicateFactory:判斷是否包含某些cookie5.HeaderRoutePredicateFactory:判斷是否包含某些header6.HostRoutePredicateFactory:判斷請求是否是訪問某個host7.MethodRoutePredicateFactory:判斷請求方式是否是指定的方式8.PathRoutePredicateFactory:判斷請求路徑是否滿足規(guī)則9.QueryRoutePredicateFactory:判斷請求參數(shù)是否包含指定的參數(shù)10.RemoteAddrRoutePredicateFactory:判斷請求ip是否在指定的范圍內(nèi)11.WeightRoutePredicateFactory:權(quán)重處理
其中BeforeRoutePredicateFactory,配置如下:
spring:
cloud:
gateway:
routes:
- id: before_route
uri: lb://service-user
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
它表示請求的時間在2017年1月20日17點42分之前的請求就滿足該路由配置,網(wǎng)關(guān)就會將請求交給service-user。
AfterRoutePredicateFactory配置如下:
spring:
cloud:
gateway:
routes:
- id: after_route
uri: lb://service-user
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
它表示請求的時間在2017年1月20日17點42分之后的請求就滿足該路由配置。
BetweenRoutePredicateFactory配置如下:
spring:
cloud:
gateway:
routes:
- id: between_route
uri: lb://service-user
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
它表示請求的時間在2017年1月20日17點42分與2017年1月21日17點42分之間的請求就滿足該路由配置。
CookieRoutePredicateFactory配置如下:
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: lb://service-user
predicates:
- Cookie=chocolate, ch.p
它表示請求中必須含有一個名字為chocolate的cookie,其值為滿足ch.p正則表達式的內(nèi)容,當(dāng)然也可以直接配置一個cookie的鍵值,鍵與值之間用逗號分隔: - Cookie=name,zhangsan 。
HeaderRoutePredicateFactory配置如下:
spring:
cloud:
gateway:
routes:
- id: header_route
uri: lb://service-user
predicates:
- Header=X-Request-Id, \d+
它表示請求中必須含有一個名為 X-Request-Id 的請求頭,其值為滿足\d+正則表達式的內(nèi)容,也可以直接配置一個鍵值: - Header=Accept-Language,zh-CN 。
HostRoutePredicateFactory配置如下:
spring:
cloud:
gateway:
routes:
- id: host_route
uri: lb://service-user
predicates:
- Host=**.somehost.org,**.anotherhost.org
它表示請求的Host必須具有**.somehost.org,**.anotherhost.org內(nèi)容。
MethodRoutePredicateFactory配置如下:
spring:
cloud:
gateway:
routes:
- id: method_route
uri: lb://service-user
predicates:
- Method=GET,POST
它表示請求的方式必須為GET或Post。
PathRoutePredicateFactory配置如下:
spring:
cloud:
gateway:
routes:
- id: path_route
uri: lb://service-user
predicates:
- Path=/red/{segment},/blue/{segment}
這個相信大家很熟悉了,就是用來匹配請求路徑的,其中segment是一個占位符,表示單層路徑匹配,比如: /red/1 、 /red/blue 、 /blue/1 、 /blue/red 都是滿足要求的。
QueryRoutePredicateFactory配置如下:
spring:
cloud:
gateway:
routes:
- id: query_route
uri: lb://service-user
predicates:
- Query=name
它表示請求中必須攜帶名為name的參數(shù),比如: http://localhost:10000/find?name=zhangsan 。
RemoteAddrRoutePredicateFactory配置如下:
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: lb://service-user
predicates:
- RemoteAddr=192.168.1.1/24
它表示請求IP必須在192.168.1.1/24網(wǎng)段內(nèi),如果你學(xué)過計算機網(wǎng)絡(luò),應(yīng)該能夠明白,不了解的也不要緊,我就簡單介紹一下。 192.168.1.1/24 采用的是斜線記法,斜杠后面的數(shù)字表示的是網(wǎng)絡(luò)前綴,即:該IP地址的前24位為網(wǎng)絡(luò)號,后8位為主機號,將IP轉(zhuǎn)換為二進制,如下:
1100 0000 1010 1000 0000 0001 0000 0001
也就是說,當(dāng)主機號全為1時,該IP為最大地址,即:192.168.1.255,所以192.168.1.1/24代表的IP段是192.168.1.1~192.168.1.255。由此可知,只要請求IP在該IP段范圍內(nèi)則是符合要求的。
WeightRoutePredicateFactory配置如下:
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: lb://service-user
predicates:
- Path=/user/**
- Weight=group1, 8
- id: weight_low
uri: lb://service-user2
predicates:
- Path=/user/**
- Weight=group1, 2
這里配置了兩個路由規(guī)則,當(dāng)請求路徑以/user開頭時這兩個規(guī)則都符合條件,但因為配置了權(quán)重分組,這兩個規(guī)則的分組均為group1,所以Gateway會按照權(quán)重比進行權(quán)衡,將80%的該請求交給service-user處理,將20%的該請求交給service-user2處理。
過濾器配置
在路由的配置中,我們還可以配置一項filter,它是Gateway提供的過濾器,可以對進入網(wǎng)關(guān)的請求和返回的響應(yīng)進行相應(yīng)的處理。與路由斷言類似,Gateway同樣提供了過濾器工廠,而且有31種之多:
這里僅僅是截取了官網(wǎng)上列舉的部分過濾器工廠,比如第一個AddRequestHeader GatewayFilterFactory,從名字就能夠看出來,這是用來添加請求頭信息的,具體配置方式如下:
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: lb://service-user
predicates:
- Path=/user/**
filters:
- AddRequestHeader=X-Request-red, blue
此時若是請求路徑滿足以/user開頭,那么該請求就會被過濾器添加上一個請求頭信息,內(nèi)容為 X-Request-red:blue ;通過配置默認過濾器,可以使過濾器對所有的路由配置生效:
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: lb://service-user
predicates:
- Path=/user/**
default-filters:
- AddRequestHeader=X-Request-red, blue
為了更加靈活地適應(yīng)各種場景,Gateway還提供了一種特殊的過濾器——GlobalFiler(全局過濾器),它的作用與default-filter類似,區(qū)別在于GlobalFilter需要我們自己去實現(xiàn),要做的就是實現(xiàn)GlobalFilter接口:
@Order(0)
@Component
public class MyGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 獲取請求參數(shù)
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, String> params = request.getQueryParams();
String tag = params.getFirst("tag");
if ("admin".equals(tag)) {
// 放行
chain.filter(exchange);
}
ServerHttpResponse response = exchange.getResponse();
// 設(shè)置狀態(tài)碼
response.setStatusCode(HttpStatus.UNAUTHORIZED);
// 攔截
return response.setComplete();
}
}
該過濾器可以實現(xiàn)對身份的校驗,只有管理員身份的請求才被放行,其它請求就會被攔截。
處理跨域
跨域是每一個前后端分離項目都需要面臨的問題,但有了網(wǎng)關(guān),我們就可以將處理跨域的流程寫在網(wǎng)關(guān)里,無需在每一個微服務(wù)中都進行配置了。
需要注意跨域問題是指瀏覽器禁止請求的發(fā)起者與服務(wù)端發(fā)生跨域的ajax請求。
配置方式如下:
spring:
cloud:
gateway:
globalcors: # 全局跨域處理
add-to-simple-url-handler-mapping: true # 解決options詢問請求被攔截的問題
cors-configurations:
'[/**]':
allowedOrigins: # 配置允許哪些網(wǎng)站跨域
- "http://localhost:8000"
- "http://localhost:9000"
allowedMethods: # 配置允許哪些請求方式跨域
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允許在請求中攜帶的頭信息
allowCredentials: true # 允許請求攜帶Cookie
maxAge: 360000 # 跨域檢測的有效時間
瀏覽器在向服務(wù)器發(fā)起請求之前,會先發(fā)送一個option請求進行詢問,查看是否滿足要求,為了防止這個詢問請求被攔截,所以需要配置 add-to-simple-url-handler-mapping: true ; [/**] 表示對所有的請求進行處理; maxAge: 360000 用于配置跨域檢測的有效時間,當(dāng)瀏覽器發(fā)送了一次option請求進行詢問并且成功后,在這段有效時間內(nèi),服務(wù)器將不再要求對瀏覽器發(fā)送過來的請求進行檢測,由此提高了性能。
本文作者:汪偉俊 為Java技術(shù)迷專欄作者 投稿,未經(jīng)允許請勿轉(zhuǎn)載。
往 期 推 薦
1、靈魂一問:你的登錄接口真的安全嗎? 2、HashMap 中這些設(shè)計,絕了~ 3、在 IntelliJ IDEA 中這樣使用 Git,賊方便了! 4、計算機時間到底是怎么來的?程序員必看的時間知識! 5、這些IDEA的優(yōu)化設(shè)置趕緊安排起來,效率提升杠杠的! 6、21 款 yyds 的 IDEA插件 7、真香!用 IDEA 神器看源碼,效率真高! 點分享
點收藏
點點贊
點在看





