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

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





