SpringCloud Gateway
简介
网关就是网络的关口。数据在网络间传输,从一个网络传输到另一网络时就需要经过网关来做数据的路由和转发以及数据安全的校验。前端请求不能直接访问微服务,而是要请求网关。

Spring Cloud Gateway的设计目标是提供一个统一的API入口,为微服务应用程序提供基于路由的访问,同时还支持常见的负载均衡、安全、监控等功能。Spring Cloud Gateway支持多种路由策略,包括基于路径、基于服务、基于请求参数等。它还支持动态路由,可以根据运行时的情况动态地添加、删除或更新路由规则。
SpringCloud Gateway工作原理(官网图片)

客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。该处理程序通过特定于请求的过滤器链运行请求。筛选器由虚线分隔的原因是,筛选器可以在发送代理请求之前和之后运行逻辑。所有“前置”过滤器逻辑均被执行。然后发出代理请求。发出代理请求后,将运行“后”过滤器逻辑。
SpringCloud Gateway核心组件
Router 路由
网关配置的基本组成模块。一个Route模块由一个ID、一个目标URI、一组断言和一组过滤器组成。如果断言为真,则路由匹配,目标URI会被访问。
Predicate 断言
它可以用来匹配来自HTTP请求的任何内容,例如headers或参数。接口包含多种默认方法,并将Predicate组合成复杂的逻辑,可以用于接口参数校验、路由转发判断等。
Filter 过滤器
使用Filter拦截和修改请求,实现对上游的响应,进行二次处理,实现横切与应用无关的功能,如安全、访问超时设置、限流等功能。
快速开始
maven导入gateway依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!-- nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
application.yml 配置
spring:
cloud:
# 注册进nacos
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
# 路由配置
routes:
#名称
- id: custom
# 转发的微服务
uri: lb://cloud-custom-service/
# 路由断言
predicates:
# 路径断言
- Path=/custom/**
# 过滤器配置
filters:
# StripPrefix过滤器,去掉第一个前缀路径
- StripPrefix=1
# 微服务名称
application:
name: cloud-gateway-service
启动nacos、cloud-gateway-service、cloud-custom-service服务

浏览器访问

进阶
代码方式注册路由
/**
* 代码方式注册路由
*/
@Bean
@Order
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(r ->
r.path("/custom/**")
.filters(f -> f.filter(customSingleFilter())
.stripPrefix(1)
)
.uri("lb://cloud-custom-service/"))
.build();
}
整合Nacos配置动态路由(生产环境推荐)
引入nacos依赖
<!-- nacos --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!--SpringCloud 2020.* 版本把bootstrap禁用了 需要自己手动引入--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency>
将application.yml名称修改为bootstrap.yml,修改内容
server: port: 9527 spring: # 微服务名称 application: name: cloud-gateway-service cloud: # 注册进nacos nacos: discovery: server-addr: 127.0.0.1:8848 config: # nacos注册中心地址 server-addr: 127.0.0.1:8848 # 指定配置文件后缀 file-extension: yml
启动nacos,新增配置文件
gateway9 spring: cloud: gateway: routes: - id: custom uri: lb://cloud-custom-service/ predicates: - Path=/custom/** filters: - StripPrefix=1
重启网关服务,查看效果
gateway10 在nacos上修改路由配置,查看效果
新增一个路由,转发到同一个服务
gateway11 spring: cloud: gateway: routes: - id: custom uri: lb://cloud-custom-service/ predicates: - Path=/custom/** filters: - StripPrefix=1 - id: hello uri: lb://cloud-custom-service/ predicates: - Path=/hello/** filters: - StripPrefix=1
不用重启网关服务,查看效果
gateway12
断言类型
Before 访问时间在指定时间戳之前
spring: cloud: gateway: routes: - id: before_route uri: https://example.org predicates: - Before=2020-01-01T00:00:00.000+08:00[Asia/Shanghai]
Between 请求时间在指定时间范围内
spring: cloud: gateway: routes: - id: between_route uri: https://example.org predicates: - Between=2020-01-01T00:00:00.000+08:00[Asia/Shanghai], 2020-01-02T00:00:00.000+08:00[Asia/Shanghai]
Cookie 请求里带有指定的Cookie
spring: cloud: gateway: routes: - id: cookie_route uri: https://example.org predicates: - Cookie=token, code
Header 请求头中包含指定请求头
spring: cloud: gateway: routes: - id: header_route uri: https://example.org predicates: - Header=X-Request-Id
Host 指定域名
spring: cloud: gateway: routes: - id: host_route uri: https://example.org predicates: - Host=**.somehost.org,**.anotherhost.org
Method 请求方法类型
spring: cloud: gateway: routes: - id: method_route uri: https://example.org predicates: - Method=GET,POST
Path 请求路径,也是最常使用的
spring: cloud: gateway: routes: - id: path_route uri: https://example.org predicates: - Path=/red/{segment},/blue/{segment}
Query 带有指定参数
spring: cloud: gateway: routes: - id: query_route uri: https://example.org predicates: - Query=green
RemoteAddr 指定请求地址
spring: cloud: gateway: routes: - id: remoteaddr_route uri: https://example.org predicates: - RemoteAddr=192.168.1.1/24
Gateway提供的过滤器
gateway过滤器文档,这里不做过多介绍,可以查看官方文档。
自定义局部过滤器
// 实现GatewayFilter 为单个过滤器
@Component
public class CustomSingleGatewayFilter implements GatewayFilter {
private static Logger logger = LoggerFactory.getLogger(CustomSingleGatewayFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
logger.info("CustomSingleGatewayFilter#filter====>");
Mono<Void> filter = chain.filter(exchange);
return filter;
}
}
自定义全局过滤器
// 实现GlobalFilter 为全局过滤器
@Component
public class CustomGatewayFilter implements GlobalFilter {
public static Logger logger = LoggerFactory.getLogger(CustomGatewayFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
logger.info("进入全局过滤器==>[{}]", exchange.getRequest().getPath());
Mono<Void> filter = chain.filter(exchange);
return filter;
}
}
限流、降级
CircuitBreaker
Sentinel
加入Sentinel依赖
spring-cloud-starter-gateway
依赖来让spring-cloud-alibaba-sentinel-gateway
模块里的 Spring Cloud Gateway 自动化配置类生效<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId> </dependency>
Sentinel 网关流控默认的粒度是 route 维度以及自定义 API 分组维度,默认不支持 URL 粒度。如需细化到 URL 粒度,请参考 网关流控文档 自定义 API 分组。
配置Sentinel控制台
spring: cloud: sentinel: transport: dashboard: localhost:8888
启动nacos、sentinel、gateway、custom几个服务
访问网关资源后查看sentinel控制台
gateway4 对router进行限流处理
gateway5 gateway6 这里不采用jemter测试了,快速刷新浏览器就可以看到效果
gateway7
自定义限流处理
代码流控设置
Set<GatewayFlowRule> ruleSet = new HashSet<>(); GatewayFlowRule custom = new GatewayFlowRule() .setGrade(RuleConstant.FLOW_GRADE_QPS) //流控类型 .setCount(1) // qps .setResource("custom"); // 资源名称,routerId或者自定义API分组id ruleSet.add(custom); GatewayRuleManager.loadRules(ruleSet);
nacos配置流控(生产环境推荐使用)
Gateway限流自定义回调
GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() { @Override public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) { ServerHttpRequest request = serverWebExchange.getRequest(); System.out.println("=========限流=============path:" + request.getPath()); Mono<ServerResponse> mono = ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS) .contentType(MediaType.APPLICATION_JSON) .bodyValue("{code:429,message:\"访问失败,请稍后再试\"}"); return mono; } });
流控结果
gateway8
全局异常处理
/**
* 全局异常处理
* 用来拦截网关自身产生的异常,比如网关路由404,找不到微服务等
* 无法拦截各个微服务返回的状态码500,404等异常
*/
@Configuration
@Order(-1)
public class GatewayGlobalExceptionHandler implements ErrorWebExceptionHandler {
public static final Logger logger = LoggerFactory.getLogger(GatewayGlobalExceptionHandler.class);
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
String path = exchange.getRequest().getPath().toString();
logger.info("全局异常处理==>[{}],异常类型=>[{}],异常描述=>[{}]", path, ex.getClass().toString(), ex.getMessage());
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
response.setStatusCode(HttpStatus.OK);
String errorMessage = "{\"code\":500,\"message\":\"处理失败\"}";
DataBufferFactory bufferFactory = response.bufferFactory();
return response.writeWith(Mono.just(bufferFactory.wrap(errorMessage.getBytes())));
}
}