官網文檔:點擊查看官網文檔
Cloud全家桶中有個很重要的組件就是網關,在1.x版本中都是采用的Zuul網關。但在2.x版本中,zuul的升級一直跳票,SpringCloud最后自己研發了一個網關替代Zuul,那就是SpringCloud Gateway一句話:gateway是原zuul1.x版的替代
。
【1】Gateway簡介
① SpringCloud Gateway 是什么
SpringCloud Gateway 是 Spring Cloud 的一個全新項目,基于 Spring 5.0+Spring Boot 2.0 和 Project Reactor 等技術開發的網關,它旨在為微服務架構提供一種簡單有效的統一的 API 路由管理方式。
SpringCloud Gateway 作為 Spring Cloud 生態系統中的網關,目標是替代 Zuul,在Spring Cloud 2.0以上版本中,沒有對新版本的Zuul 2.0以上最新高性能版本進行集成,仍然還是使用的Zuul 1.x非Reactor模式的老版本。而為了提升網關的性能,SpringCloud Gateway是基于WebFlux框架實現的,而WebFlux框架底層則使用了高性能的Reactor模式通信框架Netty。
Spring Cloud Gateway的目標提供統一的路由方式且基于 Filter 鏈的方式提供了網關基本的功能,例如:安全,監控/指標,和限流。
SpringCloud Gateway 使用的Webflux中的reactor-netty響應式編程組件,底層使用了Netty通訊框架。
② 為什么選擇SpringCloud Gateway
一方面因為Zuul1.0已經進入了維護階段,而且Gateway是SpringCloud團隊研發的,是親兒子產品,值得信賴。而且很多功能Zuul都沒有用起來也非常的簡單便捷。
Gateway是基于異步非阻塞模型上進行開發的,性能方面不需要擔心。雖然Netflix早就發布了最新的 Zuul 2.x,但 Spring Cloud 貌似沒有整合計劃。而且Netflix相關組件都宣布進入維護期,不知前景如何。
多方面綜合考慮Gateway是很理想的網關選擇。
Spring Cloud Gateway 具有如下特性:
- 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 進行構建;
- 動態路由:能夠匹配任何請求屬性;
- 可以對路由指定 Predicate(斷言)和 Filter(過濾器);
- 集成Hystrix的斷路器功能;
- 集成 Spring Cloud 服務發現功能;
- 易于編寫的 Predicate(斷言)和 Filter(過濾器);
- 請求限流功能;
- 支持路徑重寫。
③ Spring Cloud Gateway 與 Zuul的區別
在SpringCloud Finchley 正式版之前,Spring Cloud 推薦的網關是 Netflix 提供的Zuul:
1、Zuul 1.x,是一個基于阻塞 I/ O 的 API Gateway
2、Zuul 1.x 基于Servlet 2. 5使用阻塞架構它不支持任何長連接(如 WebSocket) 。Zuul 的設計模式和Nginx較像,每次 I/ O 操作都是從工作線程中選擇一個執行,請求線程被阻塞到工作線程完成,但是差別是Nginx 用C++ 實現,Zuul 用 Java 實現,而 JVM 本身會有第一次加載較慢的情況,使得Zuul 的性能相對較差。
3、Zuul 2.x理念更先進,想基于Netty非阻塞和支持長連接,但SpringCloud目前還沒有整合。 Zuul 2.x的性能較 Zuul 1.x 有較大提升。在性能方面,根據官方提供的基準測試, Spring Cloud Gateway 的 RPS(每秒請求數)是Zuul 的 1. 6 倍。
4、Spring Cloud Gateway 建立 在 Spring Framework 5、 Project Reactor 和 Spring Boot 2 之上, 使用非阻塞 API。
傳統的Web框架,比如說:struts2,springmvc等都是基于Servlet API與Servlet容器基礎之上運行的。
但是在Servlet3.1之后有了異步非阻塞的支持。而WebFlux是一個典型非阻塞異步的框架,它的核心是基于Reactor的相關API實現的。相對于傳統的web框架來說,它可以運行在諸如Netty,Undertow及支持Servlet3.1的容器上。非阻塞式+函數式編程(Spring5必須讓你使用java8)
Spring WebFlux 是 Spring 5.0 引入的新的響應式框架,區別于 Spring MVC,它不需要依賴Servlet API,它是完全異步非阻塞的,并且基于 Reactor 來實現響應式流規范。
5、Spring Cloud Gateway 還 支持 WebSocket, 并且與Spring緊密集成擁有更好的開發體驗。
④ Spring Cloud Gateway的核心概念
① Route(路由)
路由是構建網關的基本模塊,它由ID,目標URI,一系列的斷言和過濾器組成,如果斷言為true則匹配該路由。
② Predicate(斷言)
參考的是Java8的java.util.function.Predicate。
開發人員可以匹配HTTP請求中的所有內容(例如請求頭或請求參數),如果請求與斷言相匹配則進行路由。
③ Filter(過濾)
指的是Spring框架中GatewayFilter的實例,使用過濾器,可以在請求被路由前或者之后對請求進行修改。
簡單來說,web請求,通過一些匹配條件,定位到真正的服務節點。并在這個轉發過程的前后,進行一些精細化控制。
- predicate就是我們的匹配條件;
- 而filter,就可以理解為一個無所不能的攔截器。
有了這兩個元素,再加上目標uri,就可以實現一個具體的路由了
【2】Gateway工作流程
客戶端向 Spring Cloud Gateway 發出請求。然后在 Gateway Handler Mapping 中找到與請求相匹配的路由,將其發送到 Gateway Web Handler。
Handler 再通過指定的過濾器鏈來將請求發送到我們實際的服務執行業務邏輯,然后返回。
過濾器之間用虛線分開是因為過濾器可能會在發送代理請求之前(“pre”)或之后(“post”)執行業務邏輯。
Filter在“pre”類型的過濾器可以做參數校驗、權限校驗、流量監控、日志輸出、協議轉換等,
在“post”類型的過濾器中可以做響應內容、響應頭的修改,日志的輸出,流量監控等有著非常重要的作用。
【3】實踐實例
① IP端口路由
pom文件引入依賴
<!--gateway-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
yml配置:
server:port: 9527
eureka:instance:hostname: cloud-gateway-serviceclient:service-url:register-with-eureka: truefetch-registry: truedefaultZone: http://eureka7001.com:7001/eureka
spring:application:name: cloud-gatewaycloud:gateway:routes:- id: payment_routh #payment_route #路由的ID,沒有固定規則但要求唯一,建議配合服務名uri: http://localhost:8001 #匹配后提供服務的路由地址predicates:- Path=/payment/get/** # 斷言,路徑相匹配的進行路由- id: payment_routh2 #payment_route #路由的ID,沒有固定規則但要求唯一,建議配合服務名uri: http://localhost:8001 #匹配后提供服務的路由地址predicates:- Path=/payment/lb/** # 斷言,路徑相匹配的進行路由
主啟動類:
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class GateWay9527 {public static void main(String[] args) {SpringApplication.run(GateWay9527.class,args);}
}
這樣就可以實現訪問http://localhost:9527/payment/get/31
請求會路由到http://localhost:8001
,也就是 http://localhost:8001/payment/get/31
這里可以看到uri直接使用了IP和端口,其實這是不太合適的。在微服務體系中,我們推薦使用服務實例名稱進行路由。
② 服務實例路由
修改yml文件如下所示:
server:port: 9527
spring:application:name: cloud-gatewaycloud:gateway:discovery:locator:enabled: true # 開啟從服務在注冊中心動態創建路由的功能routes: # 可以配置多個路由- id: payment_routh # 路由id,沒有固定規則但要求唯一
# uri: http://localhost:8001 # 匹配后提供服務的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/payment/get/** # 路徑相匹配的進行路由- After=2020-05-26T17:07:03.043+08:00[Asia/Shanghai]
# - Cookie=username,wxh- id: payment_routh2 # 路由id,沒有
# uri: http://localhost:8001 # 匹配后提供服務的路由地址uri: lb://cloud-payment-servicepredicates:- Path=/payment/create # 路徑相匹配的進行路由eureka:instance:hostname: cloud-gateway-serviceclient:service-url:register-with-eureka: truefetch-registry: truedefaultZone: http://eureka7001.com:7001/eureka
默認情況下Gateway會根據注冊中心注冊的服務列表,以注冊中心上微服務名為路徑創建動態路由進行轉發,從而實現動態路由的功能。
需要注意的是uri的協議為lb,表示啟用Gateway的負載均衡功能。lb://serviceName
是spring cloud gateway在微服務中自動為我們創建的負載均衡uri
【4】編碼注冊路由
上面是通過配置方式注冊的路由,gateway同樣支持通過編碼方式注冊路由。
@Configuration
public class GateWayConfig {@Beanpublic RouteLocator routeLocator(RouteLocatorBuilder routeLocatorBuilder){RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();/** 代表訪問http://localhost:9527/guonei* 跳轉到http://news.baidu.com/guonei* */routes.route("route1",r->r.path("/guonei").uri("http://news.baidu.com/guonei")).build();return routes.build();}
}