一、概述
Cloud 全家桶中有个很重要的组件就是网关,在1.x版本中都是采用Zuul网关。
但是在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发一个网关代替Zuul。
那就是SpringCloud Gateway。gateway是原zuul 1.x版的替代。
Gateway是在Spring生态系统之上构建的API网关服务,基于Spring 5, Spring Boot 2和Project Reactor等技术。
Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能,例如:熔断、限流、重试等。
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 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
二、作用
反向代理
鉴权
流量控制
熔断
日志监控...
一方面因为Zuul1.0已经进入了维护阶段,而且Gateway是SpringCloud团队研发的。而且很多功能Zuul都没有,用起来也非常的简单便捷。
Gateway是基于异步非阻塞模型上进行开发的,性能方面不需要担心。虽然Netflix早就发布了最新的Zuul 2.x,但Spring Cloud貌似没有整合计划。而且Netflix相关组件都宣布进入维护期。不知前景如何?
多方面综合考虑Gateway是很理想的网关选择。
Spring Cloud Gateway 具有如下特性:
基于Spring Framework 5, Project Relactor 和 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,是一个基于阻塞1/0的API Gateway
2、Zuul 1.x基于Servlet 2. 5使用阻塞架构它不支持任何长连接(如WebSocket) Zuul的设计模式和Nginx较像,每次1/。操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是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之上,使用非阻塞APl.
5、Spring Cloud Gateway还支持WebSocket,并且与Spring紧密集成拥有更好的开发体验。
Zuul 1.x模型
Springcloud中所集成的Zuul版本,采用的是Tomcat容器,使用的是传统的Servlet IO处理模型。
Servlet的生命周期?4?servlet由servlet container进行生命周期管理。
container启动时构造servlet对象并调用servlet init()进行初始化;
container运行时接受请求,并为每个请求分配一个线程(一般从线程池中获取空闲线程)然后调用service)。
container关闭时调用servlet destory0销毁servlet;
上述模式的缺点:
servlet是一个简单的网络IO模型,当请求进入servlet container时,servlet container就会为其绑定一个线程,在并发不高的场景下这种模型是适用的。但是一旦高并发(比如抽风用jemeter压),线程数量就会上涨,而线程资源代价是昂贵的(上线文切换,内存消耗大)严重影响请求的处理时间。在一些简单业务场景下,不希望为每个request分配一个线程,只需要1个或几个线程就能应对极大并发的请求,这种业务场景下servlet模型没有优势。
所以Zuul 1.X是基于servlet之上的一个阻塞式处理模型,即spring实现了处理所有request请求的一个servlet (DispatcherServlet)并由该servlet阻塞式处理处理。所以Springcloud Zuul无法摆脱servlet模型的弊端。
Gateway模型
传统的Web框架,比如说: struts2, springmvc等都是基于Servlet APl与Servlet容器基础之上运行的。
但是,在Servlet3.1之后有了异步非阻塞的支持。而WebFlux是一个典型非阻塞异步的框架,它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty, Undertow及支持Servlet3.1的容器上。非阻塞式+函数式编程(Spring5必须让你使用java8)。
Spring WebFlux是Spring 5.0引入的新的响应式框架,区别于Spring MVC,它不需要依赖Servlet API,它是完全异步非阻塞的,并且基于Reactor来实现响应式流规范。
三、三大核心概念
1、Route(路由)
路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
2、Predicate (断言)
参考的是java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数) ,如果请求与断言相匹配则进行路由。
3、Filter(过滤)
指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
四、Gateway工作流程
Clients make requests to Spring Cloud Gateway. If the Gateway Handler Mapping determines that a request matches a Route, it is sent to the Gateway Web Handler.This handler runs sends the request through a filter chain that is specific to the request. The reason the filters are divided by the dotted line, is that filters may executellogic before the proxy request is sent or after. All "pre" filter logic is executed, then the proxy request is made. After the proxy request is made, the "post" filter logic isexecuted.
客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到GatewayWeb Handler。
Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前("pre" )或之后("post" )执行业务逻辑。
Filter在"pre"类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等。
在"post"类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。
核心逻辑:路由转发+执行过滤器链。
五、基础配置
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh # 路由ID , 没有固定的规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 # 匹配后提供服务的路由地址
uri: lb://CLOUD-PROVIDER-SERVICE
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- Host=localhost:**
- id: payment_routh2 # 路由ID , 没有固定的规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 # 匹配后提供服务的路由地址
uri: lb://CLOUD-PROVIDER-SERVICE
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
六、通过微服务实现动态路由转发
默认情况下Gateway会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。
七、Predicate的使用
Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。
Route Predicate Factories 是什么:
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。
Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个RoutePredicate工厂可以进行组合。
Spring Cloud Gateway创建Route对象时,使用RoutePredicateFactory 创建Predicate 对象, Predicate 对象可以赋值给Route. Spring Cloud Gateway包含许多内置的Route Predicate Factories。
所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合,并通过逻辑and。
常用的Route Predicate:
1、Adter Route Predicate
- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
2、Before Route Predicate
- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
- Before=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
3、Betwwen Route Predicate
- Between=2020-03-08T10:59:34.102+08:00[Asia/Shanghai] , 2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
4、Cookie Route Predicate
YML
- Cookie=username,atguigu #并且Cookie是username=zhangshuai才能访问
5、Header Route Predicate
YML
- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式
curl http://localhost:9588/paymentInfo -H "X-Request-ld:123"
6、Host Route Predicate
- Host=**.atguigu.com
7、Method Route Predicate
- Method=GET
8、Path Route Predicate
9、Query Route Predicate
- Query=username, \d+ #要有参数名称并且是正整数才能路由
示例;
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
#- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
#- Cookie=username,zhangshuai #并且Cookie是username=zhangshuai才能访问
#- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式
#- Host=**.atguigu.com
#- Method=GET
#- Query=username, \d+ #要有参数名称并且是正整数才能路由
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
八、Filter的使用
8.1、简介
Route filters allow the modification of the incoming HTTP request or outgoing HTTP response in some manner. Route filters are scoped to a particular route. Spring CloudGateway includes many built-in GatewayFilter Factories.
NOTE For more detailed examples on how to use any of the following filters, take a look at the unit tests.
路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。
Spring Cloud Gateway内置了多种路由过滤器,他们都由GatewayFilter的工厂类来产生。
8.2、Gateway的Filter
GatewayFilter(单一):
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#the-addrequestparameter-gatewayfilter-factory
31种之多
4. Route Predicate Factories
5. GatewayFilter Factories
5.1. The AddRequestHeader GatewayFilterFactory
5.2. The AddRequestParameterGatewayFilter Factory
5.3. The AddResponseHeader GatewayFilterFactory
5.4. The DedupeResponseHeaderGatewayFilter Factory
5.5. The Hystrix GatewayFilter Factory
5.6. Spring Cloud CircuitBreakerGatewayFilter Factory
5.7. The FallbackHeaders GatewayFilterFactory
5.8. The MapRequestHeader GatewayFilterFactory
5.9. The PrefixPath GatewayFilter Factory
GlobalFilter(全局):
8.3、常用的GatewayFilter:
8.4、自定义过滤器
自定义全局GlobalFilter
两个主要接口
impiemerts GlobalFilter, Ordered
作用:
全局日志记录
统一网关鉴权
...
示例:
public class GateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("------come in MyGlobalFilter : "+ new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
//合法性检验
if(uname == null){
System.out.println("----用户名为null,非法用户,请求不被接受");
//设置 response 状态码 因为在请求之前过滤的,so就算是返回NOT_FOUND 也不会返回错误页面
exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
//完成请求调用
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
//返回值是加载顺序,一般全局的都是第一位加载
@Override
public int getOrder() {
return 0;
}
}
评论