什么是Gateway
SpringCloud Gateway 是 Spring Cloud的一个全新项目,基于Spring5.0+SpringBoot2.0和ProjectReactor等技术开发的网关,旨在为微服务架构提供一种简单有效的统一的API路由管理方式。
SpringCloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Zuul。为了提升网关性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架的底层是使用了高性能的Reactor模式通信框架Netty。
SpringCloud Gateway的目标提供统一的路由方式且基于Filter链的方式提供了网关的基本功能,例如:安全,监控和限流。
既然已经有Zuul为什么要选择Gateway
1.因为Zuul1.0已经进入维护阶段,虽然Netflix发布了Zuul2.x但是SpringCloud貌似没有整合计划。而Gateway是SpringCloud团队研发的,基于异步非阻塞模型,性能方面不需要担心
2.Zuul1.x是基于Servlet2.5使用阻塞式框架,它不支持任何长连接(如WebSocket)Zuul的设计模式和Nginx比较像,每次I/O操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是Nginx用C++实现,而Zuul是用Java实现,而JVM本身会有第一次加载较慢的情况,使得Zuul的性能相对较差。
SpringCloud Gateway建立在SpringFramework5、ProjectReactor和SpringBoot2之上,并且使用非阻塞API而且还支持WebSocket,与Spring紧密集成拥有更好的开发体验。
Zuul模型和GateWay模型
SpringCloud 集成的Zuul是基于传统的Servlet IO处理模型。当请求到达时Servlet容器会分配一个线程去调用service,而这种模型是阻塞的。Zuul1.x是基于Servlet之上的一个阻塞式处理模型,既spring实现了处理所有request请求的一个servlet (DispatcherServlet)并由该servlet阻塞式处理请求。所以SpringCloud Zuul无法摆脱Servlet模型的弊端。
传统的Web框架,比如struts2,springMvc等都是基于ServletAPI与Servlet容器基础之上运行的。但是在Servlet3.1之后有了异步非阻塞的支持。而WebFlux是一个典型的异步非阻塞的框架,它的核心是基于Reactor的相关API实现的。相对于传统的Web框架,它可以运行在比如Netty,Undertow及支持Servlet3.1的容器上。是SpringFramework5引入的新的响应式框架,区别与Spring MVC,它不需要依赖ServletApi完全是异步非阻塞的,并且基于Reactor来实现响应式流规范。而SpringCloud Gateway是基于WebFlux实现的,是异步非阻塞的
Gateway
Gateway由以下三大模块组成:
路由(Route):是构建网关的基本模块,由ID、目标URI、一系列的断言和过滤器组成,如果断言是true则匹配该路由
断言(Predicate):和HTTP请求中的所有内容(如请求头或请求参数)进行匹配,如果请求与断言相匹配则进行路由
过滤器(Filter):Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者后对请求进行修改
客户端向Gateway发起请求。然后在Gateway Handler Mapping中找到与请求对应的路由,将其发送到Gateway Web Handler。Handler再通过滤链条发送到实际的服务执行业务逻辑,之后返回。Gateway的核心逻辑就是路由转发和执行过滤链
路由
1.添加对应依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2.配置路由(除了以下配置路由方式,还可以通过数据库、缓存等配置路由)
2.1. 通过yml文件配置路由
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #路由的id,没有固定规则但是要求唯一
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
2.2. 通过代码配置
@Configuration
public class GatewayConfig {
/**
* 通过访问http://localhost:9527/guonei 转发到 https://news.baidu.com/guonei
* @param builder
* @return
*/
@Bean
public RouteLocator config(RouteLocatorBuilder builder){
RouteLocatorBuilder.Builder routes = builder.routes();
routes.route("gateway_1",
r->r.path("/guonei")
.uri("https://news.baidu.com/guonei")).build();
return routes.build();
}
}
2.3也可以通过注册中心的服务名进行配置 并且可以进行负载均衡。默认情况下Gateway会根据注册中心的服务列表,以注册中心的服务名为路径创建动态路由进行转发,从而实现动态路由功能
注意:uri的协议为lb,表示启用gateway的负载均衡
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心创建路由的功能 利用微服务名进行路由
routes:
- id: payment_routh #路由的id,没有固定规则但是要求唯一
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
断言
在上述的配置中的path 还有以下配置,具体的可以看官网
Gateway官网地址
过滤器
官网给出了过滤器,分单一和全局两种,可以根据配置添加。但是实际使用中基本都使用自己写的过滤器
自定义全局过滤器
通过实现 GlobalFilter和Ordered
@Component
@Log4j
public class MyLogGetwayFilter implements GlobalFilter, Ordered {
/**
* 过滤逻辑
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("******MyLogGetwayFilter: "+new Date());
//获取请求
String name = exchange.getRequest().getQueryParams().getFirst("username");
if (StringUtils.isEmpty(name)){
log.info("******MyLogGetwayFilter: 用户名为空");
exchange.getResponse().setStatusCode(HttpStatus.ACCEPTED);
return exchange.getResponse().setComplete();
}
// 继续下一个过滤器
return chain.filter(exchange);
}
/**
* 优先级
* @return
*/
@Override
public int getOrder() {
return 0;
}
}