Gateway新一代網關
概述簡介
官網
上一代zuul 1.x
https://github.com/Netflix/zuul/wiki
當前gateway
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/
是什么
概述
Cloud全家桶中有個很重要的組件就是網關,在1.x版本中都是采用的ZuuI網關;
但在2.x版本中,zuul的升級一 直跳票, SpringCloud最后自己研發了一個網關替代Zuul,那就是SpringCloud Gateway
一句話: gateway是 原zuul1.x版的替代


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通訊框架
源碼架構

能干嘛
- 反向代理
- 鑒權
- 流量控制
- 熔斷
- 日志監控
- .....
微服務架構中網關在哪里

有Zuul了怎么又出來gateway
我們為什么選擇Gateway?
1.netflix不太靠譜,zuul2.0一直跳票,遲遲不發布
一方面因為Zuul1.0已經進 入了維護階段,而且Gateway是 SpringCloud團隊研發的,是親兒子產品,值得信賴。而且很多功能Zuul都沒有用起來也非常的簡單便捷。
Gateway是基於異步非阻塞模型上進行開發的,性能方面不需要擔心。雖然Netflix早就發布了最新的Zuul 2.x,但Spring Cloud貌似沒有整合計划。而且Netflix相關組件都宣布進入維護期;不知前景如何?
多方面綜合考慮Gateway是很理想的網關選擇。
2.SpringCloud Gateway具有如下特性
Spring Cloud Gateway具有如下特性:
- 基於Spring Framework 5, Project Reactor和Spring Boot 2.0進行構建; τ
- 動態路由:能夠匹配任何請求屬性;
- 可以對路由指定Predicate (斷言)和Filter (過濾器) ;
- 集成Hystrix的斷路器功能;
- 集成Spring Cloud服務發現功能;
- 易於編寫的Predicate (斷言)和Filter (過濾器) ;
- 請求限流功能;
- 支持路徑重寫。
3.SpringCloud Gateway與Zuul的區別
Spring Cloud Gateway與Zuul的區別
在SpringCloud Finchley正式版之前,Spring Cloud推薦的網關是Netflix 提供的Zuul:
- Zuul1.x,是一個基於阻塞I/ 0的API Gateway .
- Zuul 1.x 基於Servlet 2. 5使用阻塞架構它不支持任何長連接(如WebSocket) Zuul的設計模式和Nginx較像,每次I/ 0操作都是從工作線程中選擇一個執行, 請求線程被阻塞到工作線程完成,但是差別是Nginx用C++實現,Zuul 用Java實現,而JVM本身會有第一次加載較慢的情況,使得Zuul 的性能相對較差。
- Zuul 2.x理念更先進,想基於Netty非阻塞和支持長連接,但SpringCloud目前還沒有整合。 Zuul 2.x的性能較Zuul 1.x有較大提升。在性能方面,根據官方提供的基准測試, Spring Cloud Gateway的RPS (每秒請求數)是Zuul的1.6倍。
- Spring Cloud Gateway建立在Spring Framework 5、Project Reactor和Spring Boot2之上,使用非阻塞API。
- Spring Cloud Gateway還支持WebSocket,詛與Spring緊密集成擁有更好的開發體驗
Zuul1.x模型
Springcloud中所集成的Zuμ版本,采用的是Tomcat容器,使用的是傳統的Servlet IO處理模型。
學過尚硅谷web中期課程都知道一個題目,Servlet的生命周期?servlet由servlet container進行生命周期管理。
container啟動時構造servlet對象並調用servlet init()進行初始化;
container運行時接受請求,並為每個請求分配一個線程 (一般從線程池中獲取空閑線程) 然后調用service()。
container關閉時調用servlet destory()銷毀servlet;

上述模式的缺點:
servlet是一個簡單的網絡IO模型,當請求進入servlet container時, servlet container就會為其綁定一個線程, 在並發不高的場景下這種模型是適用的。但是一旦高並發(此如抽風用jemeter壓),線程數量就會上漲,而線程資源代價是昂貴的(. 上線文切換,內存消耗大)嚴重影響請求的處理時間。
在一些簡單業務場景下,不希望為每個request分配一個線程,只需要1個或幾個線程就能應對極大並發的請求,這種業務場景下servlet模型沒有優勢
所以Zuul 1.X是基於servlet之上的一個阻塞式處理模型,即spring實現了 處理所有request請求的一個servlet (DispatcherServlet) 並由該servlet阻塞式處理處理。所以Springcloud Zuul無法擺脫servlet模型的弊端
Gateway模型
WebFlux是什么
https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux


說明:
傳統的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來實現響應式流規范。
三大核心概念
Route(路由)
路由是構建網關的基本模塊,它由ID,目標URI,一系列的斷言和過濾器組成,如斷言為true則匹配該路由
Predicate(斷言)
參考的是Java8的java.util.function.Predicate 開發人員可以匹配HTTP請求中的所有內容(例如請求頭或請求參數),如果請求與斷言相匹配則進行路由
Filter(過濾)
指的是Spring框架中GatewayFilter的實例,使用過濾器,可以在請求被路由前或者之后對請求進行修改.
總結

web請求,通過一些匹配條件, 定位到真正的服務節點。並在這個轉發過程的前后,進行-些精細化控制。
predicate就是我們的匹配條件:而filter. 就可以理解為一個無所不能的攔截器。有了這兩個元素。再加上目標uri.就可以實現一個具體的路由了
Gateway工作流程
官網總結


客戶端向Spring Cloud Gateway發出請求。然后在Gateway Handler Mapping中找到與請求相匹配的路由,將其發送到Gateway Web Handler。
Handler再通過指定的過濾器鏈來將請求發送到我們實際的服務執行業務邏輯,然后返回。
過濾器之間用虛線分開是因為過濾器可能會在發送代理請求之前( "pre” )或之后( "post" )執行業務邏輯。
Filter在"pre" 類型的過濾器可以做參數校驗、權限校驗、流量監控、日志輸出、協議轉換等,
在"post"類型的過濾器中可以做響應內容、響應頭的修改,日志的輸出,流量監控等有着非常重要的作用。
我的理解:
gateway
先根據斷言條件找到指定的微服務,再路由轉發,過濾可以在路由轉發前或轉發后加一些自己的東西。我認為網關是整合所有模塊的不同端口,讓他們對外暴露一個統一的端口來進行映射調用使用
uri就是整合的模塊端口地址
predicated是模塊里的方法映射地址
通過端口,我們可以直接選取方法映射地址來進行調用,而不用到其模塊的信息。
核心邏輯
路由轉發+執行過濾器鏈
入門配置
新建Module
cloud-gateway-gateway9527
POM
<dependencies>
<dependency><!-- 引用自己定義的api通用包,可以使用Payment支付Entity -->
<groupId>com.eiletxie.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--熱部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
YML
server:
port: 9527
spring:
application:
name: cloud-gateway
eureka:
instance:
hostname: cloud-gateway-service
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
業務類
無
主啟動類
@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GatewayMain9527.class,args);
}
}
9527網關如何做路由映射呢???
cloud-provider-payment8001看看controller的訪問地址
- get
- lb
我們目前不想暴露8001端口,希望在8001外面套一層9527
YML新增網關配置
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # 開啟從注冊中心動態創建路由的功能,利用微服務名稱j進行路由
routes:
- id: payment_route # 路由的id,沒有規定規則但要求唯一,建議配合服務名
#匹配后提供服務的路由地址
uri: http://localhost:8001
predicates:
- Path=/payment/get/** # 斷言,路徑相匹配的進行路由
- id: payment_route2
uri: http://localhost:8001
predicates:
Path=/payment/lb/** #斷言,路徑相匹配的進行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
測試
啟動7001
啟動8001
cloud-provider-payment8001
啟動9527
訪問說明

- 添加網關前
http://localhost:8001/payment/get/31 - 添加網關后
http://localhost:9527/payment/get/31
YML配置說明
Gateway網關路由有兩種配置方式:
在配置文件yaml中配置
見前面的步驟
代碼中注入RouteLocator的Bean
官網案例

百度國內新聞網站,需要外網
https://news.baidu.com/guonei
自己寫一個
百度新聞
業務需求
通過9527網關訪問到外網的百度新聞網址
編碼
cloud-gateway-gateway9527
業務實現
package com.eiletxie.springcloud.config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author EiletXie
* @Since 2020/3/12 15:01
*/
@Configuration
public class GatewayConfig {
/**
* 配置了一個id為route-name的路由規則
* 當訪問地址 http://localhost:9527/guonei時會自動轉發到地址: http://news.baidu.com/guonei
* @param builder
* @return
*/
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder routes = builder.routes();
routes.route("path_route_eiletxie",
r -> r.path("/guonei")
.uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
@Bean
public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder routes = builder.routes();
routes.route("path_route_eiletxie2",
r -> r.path("/guoji")
.uri("http://news.baidu.com/guoji")).build();
return routes.build();
}
}
config
通過服務名實現動態
默認情況下Gatway會根據注冊中心注冊的服務列表,以注冊中心上微服務名為路徑創建動態路由進行轉發,從而實現動態路由的功能
啟動:
一個eureka7001+兩個服務提供者8001/8002
POM
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId≥spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
YML
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # 開啟從注冊中心動態創建路由的功能,利用微服務名稱進行路由
routes:
- id: payment_route # 路由的id,沒有規定規則但要求唯一,建議配合服務名
#匹配后提供服務的路由地址
#uri: http://localhost:8001
uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/** # 斷言,路徑相匹配的進行路由
- id: payment_route2
#uri: http://localhost:8001
uri: lb://cloud-payment-service
predicates:
Path=/payment/lb/** #斷言,路徑相匹配的進行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
需要注意的是uri的協議lb,表示啟用Gateway的負載均衡功能.
lb://serverName是spring cloud#gatway在微服務中自動為我們創建的負載均衡uri
測試
http://localhost:9527/payment/lb
8001/8002兩個端口切換
Predicate
是什么
啟動我們的gateway9527

Route Predicate Factories這個是什么東東

Spring Cloud Gateway將路由匹配作為Spring WebFlux HandlerMapping基礎架構的一部分。
Spring Cloud Gateway包括許多內置的Route PredicateI廠。所有這些Predicate都與HTTP請求的不同屬性匹配。 多個Route Predicate工廠可以進行組合
Spring Cloud Gateway創建Route對象時,使用 RoutePredicateFactory創建Predicate 對象,Predicate 對象可以賦值給Route。Spring Cloud Gateway包含許多內置的Route Predicate Factories.
所有這些謂詞都匹配HTTP請求的不同屬性。多種謂詞工廠可以組合,並通過邏輯and。
常用的Route Predicate

1.After Route Predicate


2.Before Route Predicate
3.Between Route Predicate
4.Cookie Route Predicate

不帶cookies訪問

帶上cookies訪問

加入curl返回中文亂碼
https://blog.csdn.net/leedee/article/details/82685636
5.Header Route Predicate


6.Host Route Predicate

7.Method Route Predicate

8.Path Route Predicate
9.Query Route Predicate

YML


10.RemoteAddr Route Predicate
11.Weight Route Predicate
小總結
ALL

說白了,Predicate就是為了實現一組匹配規則,讓請求過來找到對應的Route進行處理
Filter的使用
是什么
Spring Cloud Gateway的filter
生命周期,Only Two
- pre
- post
種類,Only Two
GatewayFilter
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#gatewayfilter-factories

GlobalFilter
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#global-filters

常用的GatewayFilter
AddRequestParameter
YML

省略
自定義過濾器
自定義全局GlobalFilter
兩個主要接口介紹
implments GlobalFilter,OrderId
能干嘛
- 全局日志記錄
- 統一網關鑒權
- .....
案例代碼
package com.atguigu.springcloud.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Date;
/**
* 全局自定義過濾器
*
* @author zzyy
* @version 1.0
* @create 2020/03/06
*/
@Component
@Slf4j
public class MyLogGatewayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("come in global filter: {}", new Date());
ServerHttpRequest request = exchange.getRequest();
String uname = request.getQueryParams().getFirst("uname");
if (uname == null) {
log.info("用戶名為null,非法用戶");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
// 放行
return chain.filter(exchange);
}
/**
* 過濾器加載的順序 越小,優先級別越高
*
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
測試
啟動

- 正確:http://localhost:9527/payment/lb?uname=z3
- 錯誤:http://localhost:9527/payment/lb
