概述
Cloud全家桶中有个很重要的组件就是网关,在1.x版本中都是采用Zuul网关;但是在2.x版本中,Zuul的升级一直跳票,Spring Cloud最后自己研发gateway替代Zuul。所以,Spring Cloud Gateway是Zuul1.x版的替代。
Spring Cloud Gateway是Spring Cloud的一个全新项目,基于Spring5.0+Spring Boot 2.0 和 Project Reaactor等技术开发的网关,它旨在微服务架构提供一种简单有效的统一的API路由管理方式。
Spring Cloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul2.0以上最新高性能版本进行集成,仍然还是使用Zuul 1.x 非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通行框架Netty。
SPringCloud Gateway的目标是提供统一的路由方式且基于Filter链的方式提供了网关基本的功能。例如:安全、监控、指标和限流
SpringCloud Gateway使用了WebFlux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架
主要功能
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
特性
- 基于Spring Framework 5, Project Reactor 和Spring Boot 2.0进行构建
- 动态路由:能够匹配任何请求属性
- 可以对路由指定Predicate(断言)和Filter(过滤器)
- 集成Hystrix断路器功能
- 集成SpringCloud微服务发现功能
- 易于编写的Predicate(断言)和Filter(过滤器)
- 请求限流功能
- 支持路径重写
非阻塞异步模型
传统的Web框架,比如说:struts2,springmvc等都是基于Servlet API与Servlet容器基础之上运行的,但是在Servlet3.1之后有了异步非阻塞的支持。二WebFlux是一个典型的非阻塞异步的框架,它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上。非阻塞+函数式编程(Spring 5以上必须让你使用java8)。Spring WebFlux是Spring 5.0 引入的新的响应式框架,区别于Spring MVC,它不需要依赖Servlet API,它是完全异步非阻塞的,并且基于Reactor来实现响应式流规范。
Gateway三大核心概念
Route 路由
路由是构建网关的基本模块,它有ID,目标URL,一系列的断言和过滤器组成,如果断言为true,则匹配改路由
Predicate 断言
参考java8的java.util.funcation.Predicate
开发人员可以匹配Http请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由转发
Filter 过滤器
指的是SPring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改
Gateway工作流程
客户端向Spring Cloud Gateway发出请求,然后在Gateway Handler Mapping中找到与请求项匹配的路由,将其发送到Gateway Web Handler。Handler在通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器可能会在发送代理请求之前或者之后执行业务逻辑。Filter在代理之前的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在代理之后执行的过滤器可以做响应内容、响应头修改、日志输出、流量监控等。
Gateway实例
新建cloud-gateway-gateway9527
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>cn.nwnu.springcloud</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>
注意
Gateway不需要引入一下pom
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
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 #匹配后提供服务的路由地址
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
启动类
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableEurekaClient
public class GatewayMain9527 {
public static void main(String args[]){
SpringApplication.run(GatewayMain9527.class, args);
}
}
通过上述配置,就可以通过9527端口来访问8001端口了
http://localhost:8001/payment/get/31
http://localhost:9527/payment/get/31
Gateway网关配置的两种方法
- 在配置文件application.yml中,见上一步
- 代码中注入RouteLocation(路由定位)的Bran
package cn.nwnu.springcloud.config;
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
//将路由"/guonei"映射到"http://news.baidu.com/guonei"
routes.route("path_route_nwnu",
r-> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
上述代码实现了路由器转发百度新闻http://localhost:9527/guonei
配置动态路由
根据服务中心注册的服务名实现
默认情况下Gateway会根据注册中心的服务列表,以注册中心上微服务名为路劲创建动态路由进行转发从而实现动态路由的功能
只需要在application.yml中开启动态路由
spring:
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
路由匹配中的uri写成微服务名称的形式
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
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
备注
开启网关后,负载均衡由网关负责
Predicate的使用
查看9527启动的后台日志
2020-03-18 18:36:58.492 INFO 18672 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [After]
2020-03-18 18:36:58.492 INFO 18672 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Before]
2020-03-18 18:36:58.492 INFO 18672 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Between]
2020-03-18 18:36:58.492 INFO 18672 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Cookie]
2020-03-18 18:36:58.492 INFO 18672 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Header]
2020-03-18 18:36:58.492 INFO 18672 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Host]
2020-03-18 18:36:58.492 INFO 18672 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Method]
2020-03-18 18:36:58.492 INFO 18672 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Path]
2020-03-18 18:36:58.492 INFO 18672 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Query]
2020-03-18 18:36:58.492 INFO 18672 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [ReadBodyPredicateFactory]
2020-03-18 18:36:58.492 INFO 18672 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [RemoteAddr]
2020-03-18 18:36:58.492 INFO 18672 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Weight]
2020-03-18 18:36:58.492 INFO 18672 --- [ restartedMain] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [CloudFoundryRouteService]
断言种类
- After 在…时间之后
- Before 在…时间之前
- Between 在…时间之间
- Cookie 需要两个参数,一个是cookie name,一个是正则表达式,路由规则会先获取对应的cookie name和正则表达式去匹配,如果匹配上就会执行路由,没有匹配上就不执行
- 带cookie访问
- 不带cookie访问
- Header 两个参数,一个属性名称,一个正则表达式
- Host 主机名
- Method 请求方式(get/post)
- Path 路径
- Query 带查询条件
- ReadBodyPredicateFactory
- RemoteAddr
- Weight
- CloudFoundryRouteService
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
- Cookie=username,zzyy
- Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
Filter的使用
概述
路由过滤器可用于修改进入的Http请求和返回的Http响应,路由过滤器只能指定路由进行使用。SpringCloud Gateway内置了多种路由过滤器,他们都由GatewayFilter的工厂来产生。
声明周期
- pre 前置
- post 后置
种类
- GatewayFilter 单一过滤(31个)
- GlobalFilter 全局过滤(14个)
自定义过滤器
实现接口 ClobalFilter,Ordered
作用
- 全局日志记录
- 统一网关鉴权
案例代码
package cn.nwnu.springcloud.filter;
@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 name = exchange.getRequest().getQueryParams().getFirst("name");
if(name == 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;
}
}