Spring Cloud Gateway
一.Gateway概述
1.何为API网关
API网关是一个服务器,是系统的唯一入口(也是微服务领域中重要的组件之一)。是大型分布式系统中,为了保护内部服务而设计的一道屏障,可以提供高性能、高可用的 API托管服务,从而帮助服务的开发者便捷地对外提供服务,而不用考虑安全控制、流量控制、审计日志等问题,统一在网关层将安全认证,流量控制,审计日志,黑白名单等实现。网关的下一层,是内部服务,内部服务只需开发和关注具体业务相关的实现。网关可以提供API发布、管理、维护等主要功能。开发者只需要简单的配置操作即可把自己开发的服务发布出去,同时置于网关的保护之下。
简单地说,API网关就是API们的管理者,例如能防止你要开放的API被恶意请求之类的…顺便再加上了一些负载均衡、身份验证等功能
- 市面上的API网关工具:
目前业界也有多款成熟的API网关可用:Kong、Tyk、Zuul、Traefik、SpringCloud Gateway等等
2.Gateway简介
2.1 Gateway官网
2.2 Gateway概述
- SpringCloud Gateway是spring官方基于Spring 5.0、Spring Boot2.0和Project Reactor等技术开发的网关,旨在为微服务架构提供简单、有效和统一的API路由管理方式
- SpringCloud Gateway作为SpringCloud生态系统中的网关,目标是替代Netflix Zuul,在SpringCloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用Zuul 1.x非Reactor模式的老版本。二为了提高网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty
- SpringCloud Gateway不仅提供统一的路由方式,并且还基于Filer链的方式提供了网关基本的功能,例如:鉴权、监控/指标、流量控制、熔断限流等
3.为什么要选择SpringCloud Gateway
Zuul 1.x 已经进入了维护阶段,而且Gateway是SpringCloud团队研发的,是亲儿子产品,值得信赖。而且很多功能Zuul都没有,用起来也非常简单便捷。Gateway是基于异步非阻塞模型上进行开发的,性能方面也不需要担心。虽然Netflix早就发布了最新的Zuul 2.x ,但SpringCloud貌似没有整合计划
3.1 Gateway特性
- 基于Spring Framework 5,Project Reactor和SpringBoot 2.0进行构建
- 动态路由:能够匹配任何请求属性
- 可以对路由指定Predicate(断言)和Filter(过滤器)
- 集成Hystrix断路器功能
- 集成SpringCloud服务发现功能
- 易于编写的Predicate(断言)和Filter(过滤器)
- 请求限流功能
- 支持路径重写
3.2 Gateway和Zuul的区别
- Zuul 1.x 是一个基于阻塞I/O的API Gateway
- Zuul 1.x 基于Servlet2.5使用阻塞架构,它不支持任何长连接(如WebSocket)Zuul的设计模式和Nginx较像,每次I/O操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是Nginx用C++实现,Zuul用java实现,而JVM本身会有第一次加载较慢的情况,使得Zuul的性能较差
- Zuul 2.x 理念更先进,相基于Netty非阻塞和支持长连接,但SpringCloud目前还没有整合,Zuul2.x的性能较Zuul1.x有较大提升。根据官方提供的基准测试,SpringCloud Gateway的RPS(每秒请求数)是Zuul的1.6倍
- SpringCloud Gateway建立在Spring Framework 5,Project Reactor和SpringBoot 2.0上,使用非阻塞API
- SpringCloud Gateway还支持WebSocket,并且与Spring紧密集成,拥有更好的开发体验
4.Gateway核心概念
- Route(路由): 路由是网关最基础的部分,路由信息有一个ID、一个目的URL、一组断言和一组Filter组成。如果断言路由为真,则说明请求的URL和配置匹配
- Predicate(断言): 参考的是java8的java.util.function.Predicate,开发人员可以匹配Http请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
- Filter(过滤器): 一个标准的Spring webFilter。SpringCloud Gateway中的Filter分为两种类型的Filter,分别是Gateway Filter和Global Filter。使用过滤器,可以在请求被路由前或者之后对请求进行修改
5.Gateway工作流程
核心逻辑:路由转发+执行过滤器链
官网总结:
客户端SpringCloud Gateway发出请求,然后在Gateway Handler Mapping中找到与之请求相匹配的路由,将其发送到Gateway Web Handler,Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会发在代理请求之前(“pre”)或之后(“post”)执行业务逻辑,这样,Filter在“pre”类型的过滤器可以做参数校验,权限校验,流量监控,日志输出,协议转换等;在“post”类型的过滤器可以做响应内容,响应头的修改,日志的输出,流量监控等有着非常重要的作用
二.Gateway入门配置
1.yml配置实现网关路由
- pom.xml
<dependencies>
<!--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>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.baidu</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</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>
- application.yml
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
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/** # 断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
- 主启动类
@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GatewayMain9527.class, args);
}
}
- 测试
可以看到通过9527端口的网关配置,可以路由到8001get、lb路径的方法
2.java配置类注入bean对象实现网关路由
- config/GatewayConfig
@Configuration
public class GatewayConfig
{
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder)
{
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_atguigu",
r -> r.path("/guonei")
.uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
- 测试
三.通过微服务名实现动态路由
- application.yml
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
- uri: lb://cloud-payment-service 需要注意的是uri的协议是lb,表示启用Gateway的负载均衡功能
四.Predicate的使用
- Path Route Predicate Factory: 就是我们上面用的,可以说是请求路径
predicates:
- Path=/payment/lb/**
- After Route Predicate Factory: 采用一个参数——日期时间,在该日期时间之后发生的请求都将被匹配
predicates:
- Path=/payment/lb/**
- After=2020-07-20T11:07:37.485+08:00[Asia/Shanghai]
- Before Route Predicate Factory: 采用一个参数——日期时间,在该日期时间之前发生的请求都将被匹配
predicates:
- Path=/payment/lb/**
- Before=2020-07-20T11:07:37.485+08:00[Asia/Shanghai]
- Between Route Predicate Factory: 有两个参数,datetime1和datetime2,在datetime1和datetime2之间的请求将被匹配,datetime2参数的实际时间必须在datetime1之后
predicates:
- Path=/payment/lb/**
- Between=2020-06-20T11:07:37.485+08:00[Asia/Shanghai],2020-07-20T11:07:37.485+08:00[Asia/Shanghai]
- Cookie Route Predicate Factory: 有两个参数,cookie名称和正则表达式,请求包含次cookie名称且正则表达式为真的将会被匹配
predicates:
- Path=/payment/lb/**
- Cookie=name, xh
- Header Route Predicate Factory: 有两个参数,header名称和正则表达式,请求包含次header名称且正则表达式为真的将会被匹配
predicates:
- Path=/payment/lb/**
- Header=X-Request-Id, \d+
- Host Route Predicate Factory: 包括一个参数:host name列表,使用Ant路径匹配规则,.作为分隔符
predicates:
- Path=/payment/lb/**
- Host=**.baidu.com
- Method Route Predicate Factory: 只包含一个参数: 需要匹配的HTTP请求方式
predicates:
- Path=/payment/lb/**
- Method=GET
- Query Route Predicat Factory: 有2个参数: 必选项 param 和可选项 regexp
predicates:
- Path=/payment/lb/**
- Query=age,\d+
- 小总结
Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理
五.Filter的使用
1.GatewayFilter(单一)
过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。过滤器可以限定作用在某些特定请求路径上。 Spring Cloud
Gateway包含许多内置的GatewayFilter工厂
GatewayFilter使用方法和Predicate类似,可以自主测试,毕竟有31种
举个例子:
- AddRequestParameter GatewayFilter Factory: 会在匹配的请求上添加请求参数
predicates:
- Path=/payment/lb/**
filters:
- AddRequestParameter=Gateway, xuhao
测试:
可以在lb方法中获取参数,我们的请求路径是没有带参数的,没有设置AddRequestParameter Filter就获取不到,设置了,就能获取到,这个主要就是测试一下,让你们可以好理解
@GetMapping(value = "/payment/lb")
public String getPaymentLB(HttpServletRequest request)
{
String gateway = request.getParameter("Gateway");
System.out.println(gateway);
return serverPort;
}
后台获取到了,说明是Filter的作用
2.GlobalFilter(全局)
GlobalFilter官网也有10种,但是我们实际开发中一般使用自定义的过滤器
- 自定义全局GlobalFilter
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered
{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{
log.info("***********come in MyLogGateWayFilter: "+new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if(uname == null)
{
log.info("*******用户名为null,非法用户,o(╥﹏╥)o");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder()
{
return 0;
}
}
不加uname
加了uname