springcloud-一篇搞定GateWay
SpringCloud GateWay简介
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基本组件
- Route(路由)
- Predicate(断言)
- Filter(过滤器)
Route(路由)
路由是构建网关的基本模块,它由路由ID,需转发到目标微服务URI,以及一系列的Predicate(断言)和Filter(过滤)组成,如果请求符合Predicate,则匹配该路由,按该路由来处理请求
Predicate(断言)
断言说简单点,就是请求匹配条件。断言是定义匹配条件,如果请求符合条件,则该请求匹配断言所属的路由
Filter(过滤器)
在SpringCloud GateWay这里的Filter的使用场景是:当请求匹配了某个路由后,需要转发到某个微服务之前或之后做一些事情
SpringCloud GateWay基本使用
SpringCloud GateWay是以微服务模块作为载体的(也会注册到注册中心上),需要创建一个maven项目作为微服务启动。关于pom.yml、主启动省略,主要说下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/** # 断言,路径相匹配的进行路由
上面gateway的配置大致说明下:有两个路由,分别是payment_routh和payment_routh2;以payment_routh为例,如果请求是匹配predicates的path,则由该路由进行处理并转发到http://localhost:8001
总结
上面的是基本的gateway配置,不过还存在问题:实际项目中某个微服务是作为集群来部署的,因此不能只写死一台服务器的地址,需要负载均衡转发到相同服务但不同实例上。下面则是动态路由的实现方式,能够解决此问题
SpringCloud GateWay之动态路由
动态路由只是uri发生了变化,如下:
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
uri配置为: lb://服务名
SpringCloud GateWay之Predicate(断言)详细配置
上面示例的Predicate,只演示了path,这只是匹配条件之一,还有其他匹配条件,如下:
下面就一些例子,如下:
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- After=2020-02-05T15:10:03.685+08:00[Asia/Shanghai]
- Before=2020-02-05T15:10:03.685+08:00[Asia/Shanghai]
- Cookie=username,zzyy
- Host=**.atguigu.com
- Query=username, \d+ # 要有参数名username并且值还要是整数才能路由
- Method=GET
上面的例子表示请求必须:请求的时间在before时间之前,after时间之后,携带key为username,value为zzyy的Cookie,客户端域名需要匹配**.atguigu.com,请求参数中得有username,且值为正整数,请求方式为get。满足上述的条件,此断言才为true,才匹配此路由
SpringCloud GateWay之Filter(过滤器))详细使用
在SpringCloud GateWay的Filter分为:GatewayFilter 和 GlobalFilter;
- GatewayFilter只针对指定的路由生效。
- GlobalFilter对全部路由生效。
GatewayFilter示例
server:
port: 9588
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能
lower-case-service-id: true #使用小写服务名,默认是大写
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://cloud-provider-payment #匹配后的目标服务地址,供服务的路由地址
#uri: http://localhost:8001 #匹配后提供服务的路由地址
filters:
- AddRequestParameter=X-Request-Id,1024 #过滤器工厂会在匹配的请求头加上一对请求头,名称为X-Request-Id值为1024
predicates:
- Path=/paymentInfo/** # 断言,路径相匹配的进行路由
- Method=GET,POST
当请求匹配了payment_routh路由,就会执行AddRequestParameter这个过滤器进行过滤
GlobalFilter示例
@Component //必须加,必须加,必须加
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered
{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{
log.info("time:"+new Date()+"\t 执行了自定义的全局过滤器: "+"MyLogGateWayFilter"+"hello");
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if (uname == null) {
System.out.println("****用户名为null,无法登录");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
//指定过滤器在过滤器的执行顺序,数值越小执行顺序越优先
public int getOrder()
{
return 0;
}
}
统一鉴权示例:
@Component //必须加,必须加,必须加
@Slf4j
public class AccessGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if(StringUtils.isEmpty(token)){
log.info("当前请求Token为空!");
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().add("Content-Type", "application/json;charset=utf-8");
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
log.info("tolen is ok");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 1;
}
}
不管请求匹配了哪个路由,都会执行gateway全局的过滤器。
总结
实际开发,GlobalFilter用的比较多,常用的使用场景是:全局日志,跨域处理,统一鉴权...