上一篇中,我们讲了这个这个zuul组件,今天我们讲一个新的网关组件Spring Cloud Gateway。为什么要讲这组件呢?因为这个SpringCloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 1.x非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。说明了Gateway的性能是要比zuul要好的,而且zuul更新太慢了。所以springcloud是推荐我们在项目中使用Gateway网关代替zuul的。
SpringCloud Gateway 官网地址:
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/
SpringCloud Gateway是什么?
SpringCloud全家桶中有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关;
但在2.x版本中,zuul的升级一直在延期,所以SpringCloud最后自己研发了一个网关替代Zuul,
那就是SpringCloud Gateway一句话:gateway是原zuul1.x版的替代组件。
SpringCloud Gateway可以做什么?
- 反向代理
- 鉴权
- 流量监控
- 日志监控
SpringCloud Gateway 与 Zuul的区别是是什么?
1、Zuul 1.x,是一个基于阻塞 I/ O 的 API Gateway
2、Zuul 1.x 基于Servlet 2. 5使用阻塞架构它不支持任何长连接(如 WebSocket) 。Zuul 的设计模式和Nginx较像,每次 I/ O 操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是Nginx 用C++ 实现,Zuul 用 Java 实现,而 JVM 本身会有第一次加载较慢的情况,使得Zuul 的性能相对较差。
3、Zuul 2.x理念更先进,想基于Netty非阻塞和支持长连接,但SpringCloud目前还没有整合。 Zuul 2.x的性能较 Zuul 1.x 有较大提升。在性能方面,根据官方提供的基准测试, Spring Cloud Gateway 的 RPS(每秒请求数)是Zuul 的 1. 6 倍。
4、Spring Cloud Gateway 建立 在 Spring Framework 5、 Project Reactor 和 Spring Boot 2 之上, 使用非阻塞 API。
SpringCloud 三个核心概念
1、Route(路由)
路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
2、Predicate(断言)
请求与断言相匹配则进行路由
3、Filter(过滤)
Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
在我们的web请求中,可以通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。predicate就是我们的匹配条件;而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了
GateWay 官网的流程图
客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之(“pre”)或之后(“post”)执行业务逻辑。Filter在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。
SpringCloud GateWay 项目整合
新建Module pcloud-gateway-gateway8888
修改pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>pcloud</artifactId>
<groupId>com.younger.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.younger.springcloud</groupId>
<artifactId>pcloud-gateway-gateway8888</artifactId>
<version>1.0-SNAPSHOT</version>
<name>pcloud-gateway-gateway8888</name>
<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>
<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>
</project>
新增 application.yml文件
server:
port: 8888
spring:
application:
name: pcloud-gateway
eureka:
instance:
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka6001:6001/eureka/
编写启动类: GateWayMain8888
package com.younger.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class GateWayMain8888 {
public static void main(String[] args) {
SpringApplication.run(GateWayMain8888.class, args);
}
}
接下来我们开始编写我们的网关配置了:
server:
port: 8888
spring:
application:
name: pcloud-gateway
cloud:
gateway:
routes:
- id: pcloud-user-service # 路由的ID,没有固定的规则但是要求需要统一,可以使用我们的服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/user/** # 断言 路径相匹配进行地址路由
eureka:
instance:
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka6001:6001/eureka/
这两个地址是想对应的。
我们现在测试一下:
测试地址:http://localhost:8888/user/1
我们不仅可以通过yml文件配置,还可以通过Java 方式配置路径:
编写GateWayConfig类:
package com.younger.springcloud.config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GateWayConfig {
/**
* 1、配置id 为 path_routh
* 2、访问http:loclhost:8888/cnds 会跳转到地址https://www.csdn.net/
* @param builder
* @return
*/
@Bean
public RouteLocator routeLocator (RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder routes = builder.routes();
routes.route("path_routh",r->r.path("/cnds").uri("https://www.csdn.net/")).build();
return routes.build();
}
}
启动项目:
测试地址: http:loclhost:8888/cnds
我们在上面的案例中的配置文件写死了访问地址 如下图:
如果我们有多台服务要怎么办呢?SpringCloud Gateway 也提供负载均衡访问服务,我们来修改下我们的配置文件:
我们把8001、8002 启动起来:
测试地址:http://localhost:8888/user/1
可以看到 8001、8002 在来回切换。
SpringCloud Gateway 断言(predicates)
SpringCloud Gateway的路由,我们大概知道是怎么回事了,那这个predicates断言又是啥呢?我们可以去SpringCloud的官网看下:
可以看到 SpringCloud Gateway 提供了很多不同的断言方式,我们刚才使用的Path 就是其中的一种,我们看下其他的断言:
1、The After Route Predicate Factory 意思就是这个地址只能在这个日期之后才可以访问
配置文件:
- After=2022-03-11T18:56:03.685+08:00[Asia/Shanghai]
如果在这个时间点前访问就会出现404
2、 The Before Route Predicate Factory 意思就是这个地址只能在这个日期之前才可以访问
- Before=2022-03-10T17:10:03.685+08:00[Asia/Shanghai]
3、 The Between Route Predicate Factory 两个时间节点之间
其他的断言配置可以参考 https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#the-cookie-route-predicate-factory 官网提供的案例,自己测试一下。
这个是它的所有的配置:
SpringCloud Gateway 过滤器(filter)
路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。
官网中的过滤器有很多
我们根据官网的提供的案例进行配置:
- AddRequestParameter=X-Request-Id,2048 #过滤器工厂会在匹配的请求头加上一对请求头,名称为X-Request-Id值为2048
SpringCloud Gateway 的全局过滤器:
我们自定义定义一个全局过滤器 MyLogGateWayFilter 类:
package com.younger.springcloud.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("执行全局的过滤器......");
String username = exchange.getRequest().getQueryParams().getFirst("username");
if (username == null) {
System.out.println("username不能为空");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
重新启动项目测试:
测试地址: http://localhost:8888/user/lb
测试地址: http://localhost:8888/user/lb?username=‘younger’