第8章:🚩【网关】路由器和过滤器:Gateway(组件4)
1 网关的作用:
反向代理路由微服务、鉴权、流量控制、熔断、日志监控等,比如返回token的时候,我们就可以在响应过滤的时候去做。
2 网关的位置:

3 网关的选择:

官方推荐使用新一代网关gateway,理由如下:
SpringCloud GetWay 是基于webFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。(异步非阻塞IO模型),扩展性也更强。
而Zuul使用的是(同步阻塞IO模型:一个请求会创建一个线程),吞吐量会低很多,不支持长连接。
4 名词解释
Predicate 断言
:这是一个
Java 8 函数Predicate。用来判断请求是否符合设定的规则。
Filter 过滤器
:这些是使用特定工厂构建的
Spring FrameworkGatewayFilter实例。在这里可以在发送下游请求之前或之后修改请求和响应。
Route 路由
:它由 ID、目标 URI、断言集合和过滤器集合定义。如果
聚合断言为真,则匹配路由。
5 原理:过滤器链:

- 客户端向 Spring Cloud Gateway 发出请求。
- 如果网关处理程序映射确定请求与路由匹配,则将其发送到网关 Web 处理程序。
- 此处理程序通过特定于请求的过滤器链运行请求。
- 过滤器用虚线划分的原因是过滤器可以在发送代理请求之前和之后运行逻辑。
- 执行所有“预”过滤器逻辑。
- 然后发出代理请求。
- 发出代理请求后,将运行“发布”过滤器逻辑。
6 实操:搭建网关服务:
1 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lzket</groupId>
<artifactId>cloud-study</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>m30-gateway-9000</artifactId>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2 yml配置:
server:
port: 9000 #
spring:
cloud:
gateway:
routes:
- id: order_gateway # # 配置的一个路由id,唯一就可以
uri: http://localhost:8033 # 配置代理路由的路径
predicates:
- Path=/order/** # 请求路径如果按照/order/开头,就匹配
- id: goods_gateway # # 配置的一个路由id,唯一就可以
uri: http://localhost:8034 # 配置代理路由的路径
predicates:
- Path=/goods/** # 请求路径如果按照/goods/开头,就匹配
🚩查看类:GatewayAutoConfiguration
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator, ConfigurationService configurationService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, gatewayFilters, properties, configurationService);
}
在这个类中会去定义【路由:断言+过滤器】
1 🚩断言工厂:默认的有这么多:
注意:每个断言工厂有名字 :名字就是RoutePredicateFactory前面那一段:比如Path RoutePredicateFactory 的名字就是 Path比如AfterRoutePredicateFactory的名字就是 After...yml中的配置格式就是:predicates:- 断言工厂名字= 值 或参数名称, 正则表达式

可以由下面的一个多多个断言组成,形成聚合断言来判断请求
spring:
cloud:
gateway:
routes:
- id: order_route # 配置的一个id,唯一就可以
uri: http://localhost:8033 # 配置代理路由的路径
predicates:
- Path=/order/** # 根据路径是否匹配来生效
- Cookie=mycookie,mycookievalue # 判断Cookie中是否又名字mycookie和值mycookievalue
- After=2017-01-20T17:42:47.789-07:00[Asia/Shanghai] # 在某时间以后生效[时区]
- Before=2017-01-20T17:42:47.789-07:00[America/Denver] # 在某时间以前生效
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver] # 这段时间生效
- Header=X-Request-Id, \d+ # 具有头信息,第二个是正则表达式
- Host=**.lzket.com,**.lzket.com # 根据主机来判断
- Method=GET,POST # 判断是不是GET或者POST方法
- Query=red, gree. # 查询参数来匹配,red是等值,gree是正则前缀
- RemoteAddr=192.168.1.1/24 # 可以根据ip来配置
还能通过权重来配置:Weight
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2
2 🚩Gateway内置过滤器:太多了,这里举一个例子
过滤器的名称等于类名去掉GatewayFilterFactory
例如:AddRequestHeaderGatewayFilterFactory的名字:AddRequestHeader
再比如:AddRequestParameterGatewayFilterFactory的名字:AddRequestParameter

头信息的过滤器:
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-red, blue #
将标头添加到所有匹配请求的下游请求标头中。
格式:filters:- 具体的过滤器名称=参数名称,参数值或正则表达式
例如:
server:
port: 9000 #
spring:
cloud:
gateway:
routes:
- id: order_gateway # # 配置的一个路由id,唯一就可以
uri: http://localhost:8033 # 配置代理路由的路径
predicates:
- Path=/order/** # 请求路径如果按照/order/开头,就匹配
- id: goods_gateway # # 配置的一个路由id,唯一就可以
uri: http://localhost:8034 # 配置代理路由的路径
predicates:
- Path=/goods/** # 请求路径如果按照/goods/开头,就匹配
filters:
- AddRequestParameter=goodsId, 333 # 添加请求参数
3 设置默认过滤器
要添加过滤器并将其应用于所有路由,您可以使用spring.cloud.gateway.default-filters.
此属性采用过滤器列表。以下清单定义了一组默认过滤器:
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Red, Default-Blue
- PrefixPath=/http
7🚩🚩全局过滤器
package com.lzket;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.CoreSubscriber;
import reactor.core.publisher.Mono;
import java.nio.charset.Charset;
import java.util.List;
/**
* @author:菩提老师
* @学习地址:lzket.com
*/
@Configuration
public class config {
@Bean
public GlobalFilter customFilter() {
return new CustomGlobalFilter();
}
}
class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("进入全局过滤器。。。");
ServerHttpRequest request = exchange.getRequest();
String goodsId = request.getQueryParams().getFirst("goodsId");
if(goodsId==null){
ServerHttpResponse response = exchange.getResponse();
//设置响应头
response.getHeaders().add("ContentType","application/json;charset=utf-8");
//设置状态码
response.setStatusCode(HttpStatus.BAD_REQUEST);
// 封装响应数据
String str = "不好意思,你的参数不存在!";
DataBuffer dataBuffer = response.bufferFactory().wrap(str.getBytes(Charset.forName("GBK")));
return response.writeWith(Mono.just(dataBuffer));
}
return chain.filter(exchange);
}
@Override
public int getOrder() {// 排序,数字越小,越先执行
return 0;
}
}
8 LoadBalancerClient过滤器 负载均衡过滤器:
上面的方式是uri写死的,不能实现负载均衡,我们需要按照微服务的名称实现负载均衡
pom文件加依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
server:
port: 9000 #
spring:
cloud:
gateway:
routes:
# - id: order_gateway # # 配置的一个路由id,唯一就可以
# uri: http://localhost:8033 # 配置代理路由的路径
# predicates:
# - Path=/order/** # 请求路径如果按照/order/开头,就匹配
# - id: goods_gateway # # 配置的一个路由id,唯一就可以
# uri: http://localhost:8034 # 配置代理路由的路径
# predicates:
# - Path=/goods/** # 请求路径如果按照/goods/开头,就匹配
# filters:
# - AddRequestParameter=goodsId, 333 # 添加请求参数
- id: order_gateway
uri: lb://order-nacos-loadbalancer # 负载均衡写法
predicates:
- Path=/order/**
- id: goods_gateway
uri: http://goods-nacos-loadbalancer # 负载均衡写法
predicates:
- Path=/goods/**
filters:
- AddRequestParameter=goodsId, 999
nacos:
discovery:
server-addr: 192.168.124.10:8848 #配置Nacos地址
username: nacos # 默认的用户名和密码,可以到nacos控制台进行更改
password: nacos # 默认的用户名和密码,可以到nacos控制台进行更改
loadbalancer:
nacos:
enabled: true # 开启nacos的负载均衡随机权重算法
9 流畅的 Java 路由 API 配置(代码配置):🚩
package com.lzket;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.AddRequestParameterGatewayFilterFactory;
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;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.CoreSubscriber;
import reactor.core.publisher.Mono;
import java.nio.charset.Charset;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author:菩提老师
* @学习地址:lzket.com
*/
@Configuration
public class config {
@Bean
public GlobalFilter customFilter() {
return new CustomGlobalFilter();
}
// static imports from GatewayFilters and RoutePredicates
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder routes = builder.routes();
// 代替yml配置
return routes
.route("user", r -> r.path("/user/**").uri("lb://user-nacos-loadbalancer"))
.route("goods", r -> r.path("/goods/**").uri("lb://goods-nacos-loadbalancer"))
.route("order", r -> r.path("/order/**").uri("lb://order-nacos-loadbalancer"))
.build();
// 在这里可以配置路由的各种断言 和多个路由
// return builder.routes()
// .route(r -> r.host("lzket.com").and()
// .path("/user/**")
// .filters(f -> f.addResponseHeader("X-TestHeader", "foobar"))
// .uri("http://lzket.com/user")
// )
// .route(r -> r.order(-1)
// .path("/goods/**")
.and()
.after().and()
.before().and()
.between().and()
.cookie().and()
.weight().and()
.method()
// .filters(f -> f.addRequestParameter("goodsId","999"))
// .uri("http://httpbin.org:80")
// )
// .build();
}
}
class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("进入全局过滤器。。。");
ServerHttpRequest request = exchange.getRequest();
String goodsId = request.getQueryParams().getFirst("goodsId");
// if(goodsId==null){
// ServerHttpResponse response = exchange.getResponse();
// //设置响应头
// response.getHeaders().add("ContentType","application/json;charset=utf-8");
// //设置状态码
// response.setStatusCode(HttpStatus.BAD_REQUEST);
// // 封装响应数据
// String str = "不好意思,你的参数不存在!";
// DataBuffer dataBuffer = response.bufferFactory().wrap(str.getBytes(Charset.forName("GBK")));
// return response.writeWith(Mono.just(dataBuffer));
//
// }
return chain.filter(exchange);
}
@Override
public int getOrder() {// 排序,数字越小,越先执行
return 0;
}
}
10 全局超时配置
spring:
cloud:
gateway:
httpclient:
connect-timeout: 1000 # 设置全局的连接超时时间
response-timeout: 5s # 设置全局的服务响应时间
注意:如果是nacos的话,gateway需要引入负载均衡组件,不然会报错503,因为网关默认的负载均衡的生命周期是【可选】:
true
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>