这是我参与更文挑战的第 14 天,活动详情查看: 更文挑战
Gateway
一、概述
Gateway(网关)主要提供了一种简单而有效的方式来对API进行路由,以及一些强大的过滤功能,如:熔断、断流、重试等。
Spring Cloud Gateway 作为Spring Cloud生态系统中的网关,目标是替代Zuul,Cloud Gateway是基于WebFlux框架实现的,而web Flux框架底层使用了高性能的Reactor模式通信框架Netty,基于异步非阻塞模型上进行开发的。
Spring Cloud Gateway提供了统一的路由方式且基于Filter链的方式提供了网关的基本功能,如:安全、监控、指标和限流。
Gateway三大概念:
- Route(路由):是构成网关的基本模块,由id、目标url、一系列断言和过滤器组成,如果断言为true匹配该路由。
- Predicate(断言):可以匹配Http请求中的所有内容,如果请求与断言相匹配则进行路由。
- Filter(过滤):使用过滤器,可以在请求在路由前或后对请求进行修改。
执行流程:客户端向Gateway发送请求,如何在Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler。Handler再通过指定的过滤器链将请求发送到实际的服务器执行业务逻辑,如何返回。在前置过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等。后置过滤器可以做响应内容、响应头的修改。
二、搭建环境
依赖,注意:引入gateway后不可再添加web的依赖了
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
配置文件配置路由规则
server:
port: 9527
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes: # 可以配置多个路由
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/getById/** #断言,路径相匹配的进行路由
- id: payment_routh2
uri: http://localhost:8001
predicates:
- Path=/payment/getPort/**
测试
访问http://localhost:9527/payment/getById/**即可访问到8001访问下的方法。
或者使用编码方式配置路由规则
/**
* 使用配置类方法配置网关路由
*/
@SpringBootConfiguration
public class GatewayConfig {
/**
* 配置了一个路由名字为path_route_yylm的规则
* 访问http://localhost:9527/guonei跳转到百度新闻国内新闻
* @param builder
* @return
*/
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder routes = builder.routes();
routes.route("path_route_yylm",r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
三、动态路由
把gateway网关也注册到注册中心,然后通过注册中心中的微服务名称调用具体的服务。
配置
gateway:
discovery:
locator:
enabled: true # 开启动态创建路由,使用注册中心的服务名称进行路由
routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://CLOUD-PAYMENT-SERVICE #使用动态路由,提供服务的服务名称
启动8001和8002两个微服务,通过lb://CLOUD-PAYMENT-SERVICE即可访问到具体服务,并且可以实现负载均衡。
四、Predicate(断言)的使用
Gateway包括许多内置的Route Predicate工厂,所有Predicate都与Http请求的不同属性匹配。 Predicate就是实现一组匹配规则,让请求找到对应的Route进行匹配
时间级别的
predicates:
- Path=/payment/getPort/**
- After=2020-12-04T00:14:13.512+08:00[Asia/Shanghai] # 指定时间后生效
# - Before=2020-12-04T00:10:13.512+08:00[Asia/Shanghai] # 指定时间前生效
cookie级别
predicates:
- Path=/payment/getPort/**
- After=2020-12-04T00:14:13.512+08:00[Asia/Shanghai] # 指定时间后生效
# - Before=2020-12-04T00:14:13.512+08:00[Asia/Shanghai] # 指定时间前生效
- Cookie=username,yylm # 必须携带指定cookie
Cookie Route Predicates需要两个参数,一个是cookie name,一个是正则表达式,路由规则会通过获取对应的cookie name和正则表达式去匹配,匹配上执行,没有匹配上不执行。
在访问时就必须携带Cookie 值为username=yylm
还有Header/Host Route Predicates 和Cookie的差不多,需要携带指定的请求头才能访问成功。
其他级别
- Method:- Method=GET //指定匹配的访问方法
- Path:- Path=xxxx //指定匹配的访问路径
- Query: -Query=username, \d+ //指定参数名要有username,并且值为整数才能路由
五、Gateway的Filter
路由过滤器可以修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用,内置了多种路由过滤器。
内置过滤器,和Predicates的使用方法相似,使用更多的是自定义过滤器。
filters:
- AddRequestHeader=X-Request-red, blue
自定义全局过滤器
@Component
public class MyLogGatewayFilter implements GlobalFilter, Ordered {
public static final Logger LOGGER = LoggerFactory.getLogger(MyLogGatewayFilter.class);
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
LOGGER.info("进到MyLogGatewayFilter的filter方法:"+ new Date());
//从请求中获取请求参数
String name = exchange.getRequest().getQueryParams().getFirst("uname");
//如果没有该参数就给出提示
if (name == null) {
LOGGER.info("非法用户");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
public int getOrder() {
return 0;
}
}
测试:使用http://localhost:9527/payment/getById/31?uname=lm,携带请求参数uname才可以请求进来。否则被拒绝。