理论
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 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
gateway之所以性能好,因为底层使用WebFlux,而webFlux底层使用netty通信(NIO)
三大核心
路由
路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由,说白了就是根据某些规则,将请求发送到指定服务上
实现
- 建模块
cloud_gateway_9527
- 导依赖
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
- 添加yml文件
server:
port: 9527
spring:
application:
name: cloud-gateway
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka # 注册地址和端口
instance:
hostname: cloud-gateway-service
- 主启动类,添加
@EnableEurekaClient
、@EnableDiscoveryClient
注解 - 我们目前不想暴露cloud-provider-payment8001服务的8001端口,希望在8001端口外面套一层9527,添加配置,为8001服务的访问地址做了路由匹配
spring:
cloud:
gateway:
routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2 #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
表示当访问localhost:9527/payment/get/1时,路由到localhost:8001/payment/get/1
6. 启动测试
重构
上面的实现将访问地址和端口写死了,接下来实现路由侧的动态路由
和负载均衡
修改GateWay服务的yml配置文件,注释掉之前的配置的uri路由地址
,改为服务名uri: lb://cloud-payment-service
官网文档:Spring Cloud Netflix Ribbon会在定义lb前缀的目标URI上实现负载均衡
spring:
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 #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
启动cloud-provider-payment8001和cloud-provider-payment8002服务,启动网关服务,访问http://localhost:9527/payment/lb
,测试是否实现了根据服务名完成了动态路由,以及是否实现了负载均衡。
断言
参考的是Java8的java.util.function.Predicate
,开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
之前在配置文件中配置了断言,这个-Path断言表示,如果外部访问路径是指定路径,就路由到指定微服务上
断言类型
其中有时间相关的断言,时区时间格式可由ZoneDateTime这个类获得
public static void main(string[] args){
ZonedDateTime time = ZonedDateTime.now();//默认时区
System.out.println(time);
}
断言就是实现一组匹配规则,让请求过来找到对应的Route进行处理
过滤器
指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用,Spring Cloud Gateway内置了多种路由过滤器,他们都由GatewayFactory的工厂类来产生。
过滤器生命周期:在请求进入路由之前,和处理请求完成,再次到达路由之前
自定义过滤器
在网关服务中定义一个过滤器类,实现 GlobalFilter
,Ordered
这两个接口
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("***********************come in MyLogGateWayFilter"+ new Date());
// 要求请求必须带上uname的查询参数
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if(uname == null){
log.info("**********用户名为null***********滚");
// 设置相应状态为NOT_ACCEPTABLE
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
// 如果通过,则跳到下一个过滤链
return chain.filter(exchange);
}
// 表示加载过滤器的优先级,数字越小优先级越高
@Override
public int getOrder() {
return 0;
}
重启网关服务,访问测试:http://localhost:9527/payment/lb?uname=kzj
成功,若不带上uname参数则测试失败
扩展:curl命令
curl + 微服务地址 + 参数
curl http://localhost:9527/payment/lb
这是一个get请求
curl http://localhost:9527/payment/lb --cookie "username=kzj"
带上cookie