javascript
微服务网关Zuul迁移到Spring Cloud Gateway
https://juejin.im/post/5ba8daa56fb9a05cfe486ebf
背景
在之前的文章中,我們介紹過微服務(wù)網(wǎng)關(guān)Spring Cloud Netflix Zuul,前段時(shí)間有兩篇文章專門介紹了Spring Cloud的全新項(xiàng)目Spring Cloud Gateway,以及其中的過濾器工廠。本文將會(huì)介紹將微服務(wù)網(wǎng)關(guān)由Zuul遷移到Spring Cloud Gateway。
Spring Cloud Netflix Zuul是由Netflix開源的API網(wǎng)關(guān),在微服務(wù)架構(gòu)下,網(wǎng)關(guān)作為對外的門戶,實(shí)現(xiàn)動(dòng)態(tài)路由、監(jiān)控、授權(quán)、安全、調(diào)度等功能。
Zuul基于servlet 2.5(使用3.x),使用阻塞API。 它不支持任何長連接,如websockets。而Gateway建立在Spring Framework 5,Project Reactor和Spring Boot 2之上,使用非阻塞API。 比較完美地支持異步非阻塞編程,先前的Spring系大多是同步阻塞的編程模式,使用thread-per-request處理模型。即使在Spring MVC Controller方法上加@Async注解或返回DeferredResult、Callable類型的結(jié)果,其實(shí)仍只是把方法的同步調(diào)用封裝成執(zhí)行任務(wù)放到線程池的任務(wù)隊(duì)列中,還是thread-per-request模型。Gateway 中Websockets得到支持,并且由于它與Spring緊密集成,所以將會(huì)是一個(gè)更好的開發(fā)體驗(yàn)。
在一個(gè)微服務(wù)集成的項(xiàng)目中microservice-integration,我們整合了包括網(wǎng)關(guān)、auth權(quán)限服務(wù)和backend服務(wù)。提供了一套微服務(wù)架構(gòu)下,網(wǎng)關(guān)服務(wù)路由、鑒權(quán)和授權(quán)認(rèn)證的項(xiàng)目案例。整個(gè)項(xiàng)目的架構(gòu)圖如下:
?
?
具體參見:微服務(wù)架構(gòu)中整合網(wǎng)關(guān)、權(quán)限服務(wù)。本文將以該項(xiàng)目中的Zuul網(wǎng)關(guān)升級作為示例。
Zuul網(wǎng)關(guān)
在該項(xiàng)目中,Zuul網(wǎng)關(guān)的主要功能為路由轉(zhuǎn)發(fā)、鑒權(quán)授權(quán)和安全訪問等功能。
Zuul中,很容易配置動(dòng)態(tài)路由轉(zhuǎn)發(fā),如:
zuul: ribbon: eager-load: enabled: true #zuul饑餓加載 host: maxTotalConnections: 200 maxPerRouteConnections: 20 routes: user: path: /user/** ignoredPatterns: /consul serviceId: user sensitiveHeaders: Cookie,Set-Cookie 復(fù)制代碼默認(rèn)情況下,Zuul在請求路由時(shí),會(huì)過濾HTTP請求頭信息中的一些敏感信息,這里我們不過多介紹。
網(wǎng)關(guān)中還配置了請求的鑒權(quán),結(jié)合Auth服務(wù),通過Zuul自帶的Pre過濾器可以實(shí)現(xiàn)該功能。當(dāng)然還可以利用Post過濾器對請求結(jié)果進(jìn)行適配和修改等操作。
除此之外,還可以配置限流過濾器和斷路器,下文中將會(huì)增加實(shí)現(xiàn)這部分功能。
遷移到Spring Cloud Gateway
筆者新建了一個(gè)gateway-enhanced的項(xiàng)目,因?yàn)樽兓艽?#xff0c;不適合在之前的gateway項(xiàng)目基礎(chǔ)上修改。實(shí)現(xiàn)的主要功能如下:路由轉(zhuǎn)發(fā)、權(quán)重路由、斷路器、限流、鑒權(quán)和黑白名單等。本文基于主要實(shí)現(xiàn)如下的三方面功能:
- 路由斷言
- 過濾器(包括全局過濾器,如斷路器、限流等)
- 全局鑒權(quán)
- 路由配置
- CORS
依賴
本文采用的Spring Cloud Gateway版本為2.0.0.RELEASE。增加的主要依賴如下,具體的細(xì)節(jié)可以參見Github上的項(xiàng)目。
<dependencies><dependency><groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> <!--<version>2.0.1.RELEASE</version>--> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-gateway-webflux</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 復(fù)制代碼路由斷言
Spring Cloud Gateway對于路由斷言、過濾器和路由的定義,同時(shí)支持配置文件的shortcut和Fluent API。我們將以在本項(xiàng)目中實(shí)際使用的功能進(jìn)行講解。
路由斷言在網(wǎng)關(guān)進(jìn)行轉(zhuǎn)發(fā)請求之前進(jìn)行判斷路由的具體服務(wù),通常可以根據(jù)請求的路徑、請求體、請求方式(GET/POST)、請求地址、請求時(shí)間、請求的HOST等信息。我們主要用到的是基于請求路徑的方式,如下:
spring: cloud: gateway: routes: - id: service_to_web uri: lb://authdemo predicates: - Path=/demo/** 復(fù)制代碼我們定義了一個(gè)名為service_to_web的路由,將請求路徑以/demo/**的請求都轉(zhuǎn)發(fā)到authdemo服務(wù)實(shí)例。
我們在本項(xiàng)目中路由斷言的需求并不復(fù)雜,下面介紹通過Fluent API配置的其他路由斷言:
在如上的路由定義中,我們配置了以及請求HOST、請求頭部和請求的參數(shù)。在一個(gè)路由定義中,可以配置多個(gè)斷言,采取與或非的關(guān)系判斷。
以上增加的配置僅作為擴(kuò)展,讀者可以根據(jù)自己的需要進(jìn)行配置相應(yīng)的斷言。
過濾器
過濾器分為全局過濾器和局部過濾器。我們通過實(shí)現(xiàn)GlobalFilter、GatewayFilter接口,自定義過濾器。
全局過濾器
本項(xiàng)目中,我們配置了如下的全局過濾器:
- 基于令牌桶的限流過濾器
- 基于漏桶算法的限流過濾器
- 全局?jǐn)嗦菲?/li>
- 全局鑒權(quán)過濾器
定義全局過濾器,可以通過在配置文件中,增加spring.cloud.gateway.default-filters,或者實(shí)現(xiàn)GlobalFilter接口。
基于令牌桶的限流過濾器
隨著時(shí)間流逝,系統(tǒng)會(huì)按恒定 1/QPS 時(shí)間間隔(如果 QPS=100,則間隔是 10ms)往桶里加入 Token,如果桶已經(jīng)滿了就不再加了。每個(gè)請求來臨時(shí),會(huì)拿走一個(gè) Token,如果沒有 Token 可拿了,就阻塞或者拒絕服務(wù)。
令牌桶的另外一個(gè)好處是可以方便的改變速度。一旦需要提高速率,則按需提高放入桶中的令牌的速率。一般會(huì)定時(shí)(比如 100 毫秒)往桶中增加一定數(shù)量的令牌,有些變種算法則實(shí)時(shí)的計(jì)算應(yīng)該增加的令牌的數(shù)量。
在Spring Cloud Gateway中提供了默認(rèn)的實(shí)現(xiàn),我們需要引入redis的依賴:
<dependency><groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency> 復(fù)制代碼并進(jìn)行如下的配置:
spring: redis: host: localhost password: pwd port: 6378 cloud: default-filters: - name: RequestRateLimiter args: key-resolver: "#{@remoteAddrKeyResolver}" rate-limiter: "#{@customRateLimiter}" # token 復(fù)制代碼注意到,在配置中使用了兩個(gè)SpEL表達(dá)式,分別定義限流鍵和限流的配置。因此,我們需要在實(shí)現(xiàn)中增加如下的配置:
在如上的實(shí)現(xiàn)中,初始化好RedisRateLimiter和RemoteAddrKeyResolver兩個(gè)Bean實(shí)例,RedisRateLimiter是定義在Gateway中的redis限流屬性;而RemoteAddrKeyResolver使我們自定義的,基于請求的地址作為限流鍵。如下為該限流鍵的定義:
public class RemoteAddrKeyResolver implements KeyResolver { private static final Logger LOGGER = LoggerFactory.getLogger(RemoteAddrKeyResolver.class); public static final String BEAN_NAME = "remoteAddrKeyResolver";RemoteAddrKeyResolver實(shí)現(xiàn)了KeyResolver接口,覆寫其中定義的接口,返回值為請求中的地址。
如上,即實(shí)現(xiàn)了基于令牌桶算法的鏈路過濾器,具體細(xì)節(jié)不再展開。
基于漏桶算法的限流過濾器
漏桶(Leaky Bucket)算法思路很簡單,水(請求)先進(jìn)入到漏桶里,漏桶以一定的速度出水(接口有響應(yīng)速率),當(dāng)水流入速度過大會(huì)直接溢出(訪問頻率超過接口響應(yīng)速率),然后就拒絕請求,可以看出漏桶算法能強(qiáng)行限制數(shù)據(jù)的傳輸速率。
這部分實(shí)現(xiàn)讀者參見GitHub項(xiàng)目以及文末配套的書,此處略過。
全局?jǐn)嗦菲?/h5>
關(guān)于Hystrix斷路器,是一種服務(wù)容錯(cuò)的保護(hù)措施。斷路器本身是一種開關(guān)裝置,用于在電路上保護(hù)線路過載,當(dāng)線路中有發(fā)生短路狀況時(shí),斷路器能夠及時(shí)的切斷故障電路,防止發(fā)生過載、起火等情況。
微服務(wù)架構(gòu)中,斷路器模式的作用也是類似的,當(dāng)某個(gè)服務(wù)單元發(fā)生故障之后,通過斷路器的故障監(jiān)控,直接切斷原來的主邏輯調(diào)用。關(guān)于斷路器的更多資料和Hystrix實(shí)現(xiàn)原理,讀者可以參考文末配套的書。
這里需要引入spring-cloud-starter-netflix-hystrix依賴:
<dependency><groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <optional>true</optional> </dependency> 復(fù)制代碼并增加如下的配置:
default-filters: - name: Hystrix args: name: fallbackcmd fallbackUri: forward:/fallbackcontroller 復(fù)制代碼如上的配置,將會(huì)使用HystrixCommand打包剩余的過濾器,并命名為fallbackcmd,我們還配置了可選的參數(shù)fallbackUri,降級邏輯被調(diào)用,請求將會(huì)被轉(zhuǎn)發(fā)到URI為/fallbackcontroller的控制器處理。定義降級處理如下:
全局鑒權(quán)過濾器
我們通過自定義一個(gè)全局過濾器實(shí)現(xiàn),對請求合法性的鑒權(quán)。具體功能不再贅述了,通過實(shí)現(xiàn)GlobalFilter接口,區(qū)別的是Webflux傳入的是ServerWebExchange,通過判斷是不是外部接口(外部接口不需要登錄鑒權(quán)),執(zhí)行之前實(shí)現(xiàn)的處理邏輯。
public class AuthorizationFilter implements GlobalFilter, Ordered { //....定義好全局過濾器之后,只需要配置一下即可:
局部過濾器
我們常用的局部過濾器有增減請求和相應(yīng)頭部、增減請求的路徑等多種過濾器。我們這里用到的是去除請求的指定前綴,這部分前綴只是用戶網(wǎng)關(guān)進(jìn)行路由判斷,在轉(zhuǎn)發(fā)到具體服務(wù)時(shí),需要去除前綴:
- id: service_to_user uri: lb://user order: 8000 predicates: - Path=/user/** filters: - AddRequestHeader=X-Request-Foo, Bar - StripPrefix=1 復(fù)制代碼還可以通過Fluent API,如下:
除了設(shè)置前綴過濾器外,我們還設(shè)置了重試過濾器,可以參見:Spring Cloud Gateway中的過濾器工廠:重試過濾器
路由配置
路由定義在上面的示例中已經(jīng)有列出,可以通過配置文件和定義RouteLocator的對象。這里需要注意的是,配置中的uri屬性,可以是具體的服務(wù)地址(IP+端口號),也可以是通過服務(wù)發(fā)現(xiàn)加上負(fù)載均衡定義的:lb://user,表示轉(zhuǎn)發(fā)到user的服務(wù)實(shí)例。當(dāng)然這需要我們進(jìn)行一些配置。
引入服務(wù)發(fā)現(xiàn)的依賴:
<dependency><groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> 復(fù)制代碼網(wǎng)關(guān)中開啟spring.cloud.gateway.discovery.locator.enabled=true即可。
CORS配置
在Spring 5 Webflux中,配置CORS,可以通過自定義WebFilter實(shí)現(xiàn):
private static final String ALLOWED_HEADERS = "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN";private static final String ALLOWED_METHODS = "GET, PUT, POST, DELETE, OPTIONS"; private static final String ALLOWED_ORIGIN = "*"; private static final String MAX_AGE = "3600";上述代碼實(shí)現(xiàn)比較簡單,讀者根據(jù)實(shí)際的需要配置ALLOWED_ORIGIN等參數(shù)。
總結(jié)
在高并發(fā)和潛在的高延遲場景下,網(wǎng)關(guān)要實(shí)現(xiàn)高性能高吞吐量的一個(gè)基本要求是全鏈路異步,不要阻塞線程。Zuul網(wǎng)關(guān)采用同步阻塞模式不符合要求。
Spring Cloud Gateway基于Webflux,比較完美地支持異步非阻塞編程,很多功能實(shí)現(xiàn)起來比較方便。Spring5必須使用java 8,函數(shù)式編程就是java8重要的特點(diǎn)之一,而WebFlux支持函數(shù)式編程來定義路由端點(diǎn)處理請求。
通過如上的實(shí)現(xiàn),我們將網(wǎng)關(guān)從Zuul遷移到了Spring Cloud Gateway。在Gateway中定義了豐富的路由斷言和過濾器,通過配置文件或者Fluent API可以直接調(diào)用和使用,非常方便。在性能上,也是勝于之前的Zuul網(wǎng)關(guān)。
欲了解更詳細(xì)的實(shí)現(xiàn)原理和細(xì)節(jié),大家可以關(guān)注筆者本月底即將出版的《Spring Cloud 微服務(wù)架構(gòu)進(jìn)階》,本書中對Spring Cloud Finchley.RELEASE版本的各個(gè)主要組件進(jìn)行原理講解和實(shí)戰(zhàn)應(yīng)用,網(wǎng)關(guān)則是基于最新的Spring Cloud Gateway。
?
?
本文的源碼地址:
GitHub:github.com/keets2012/m… 或者 碼云:gitee.com/keets/micro…
作者:aoho
鏈接:https://juejin.im/post/5ba8daa56fb9a05cfe486ebf
來源:掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/articles/10402519.html
總結(jié)
以上是生活随笔為你收集整理的微服务网关Zuul迁移到Spring Cloud Gateway的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: API Gateways – An Ev
- 下一篇: api网关揭秘--spring clou