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