SpringCloud Gateway 详解
SpringCloud Gateway 详解
一、网关简介
1、服务网关介绍
1.1 API网关介绍
API网关是一个服务器,是系统的唯一入口。 从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、协议转换、限流熔断、静态响应处理。
API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。
1.2 网关主要功能
微服务网关作为微服务后端服务的统一入口,它可以统筹管理后端服务,主要分为数据平面和控制平面:
-
数据平面主要功能是接入用户的HTTP请求和微服务被拆分后的聚合。使用微服务网关统一对外暴露后端服务的API和契约,路由和过滤功能正是网关的核心能力模块。另外,微服务网关可以实现拦截机制和专注跨横切面的功能,包括协议转换、安全认证、熔断限流、灰度发布、日志管理、流量监控等。
-
控制平面主要功能是对后端服务做统一的管控和配置管理。例如,可以控制网关的弹性伸缩;可以统一下发配置;可以对网关服务添加标签;可以在微服务网关上通过配置Swagger功能统一将后端服务的API契约暴露给使用方,完成文档服务,提高工作效率和降低沟通成本。
-
路由功能:路由是微服务网关的核心能力。通过路由功能微服务网关可以将请求转发到目标微服务。在微服务架构中,网关可以结合注册中心的动态服务发现,实现对后端服务的发现,调用方只需要知道网关对外暴露的服务API就可以透明地访问后端微服务。
-
负载均衡:API网关结合负载均衡技术,利用Eureka或者Consul等服务发现工具,通过轮询、指定权重、IP地址哈希等机制实现下游服务的负载均衡。
-
统一鉴权:一般而言,无论对内网还是外网的接口都需要做用户身份认证,而用户认证在一些规模较大的系统中都会采用统一的单点登录(Single Sign On)系统,如果每个微服务都要对接单点登录系统,那么显然比较浪费资源且开发效率低。API网关是统一管理安全性的绝佳场所,可以将认证的部分抽取到网关层,微服务系统无须关注认证的逻辑,只关注自身业务即可。
-
协议转换:API网关的一大作用在于构建异构系统,API网关作为单一入口,通过协议转换整合后台基于REST、AMQP、Dubbo等不同风格和实现技术的微服务,面向Web Mobile、开放平台等特定客户端提供统一服务。
-
指标监控:网关可以统计后端服务的请求次数,并且可以实时地更新当前的流量健康状态,可以对URL粒度的服务进行延迟统计,也可以使用Hystrix Dashboard查看后端服务的流量状态及是否有熔断发生。
-
限流熔断:在某些场景下需要控制客户端的访问次数和访问频率,一些高并发系统有时还会有限流的需求。在网关上可以配置一个阈值,当请求数超过阈值时就直接返回错误而不继续访问后台服务。当出现流量洪峰或者后端服务出现延迟或故障时,网关能够主动进行熔断,保护后端服务,并保持前端用户体验良好。
-
黑白名单:微服务网关可以使用系统黑名单,过滤HTTP请求特征,拦截异常客户端的请求,例如DDoS攻击等侵蚀带宽或资源迫使服务中断等行为,可以在网关层面进行拦截过滤。比较常见的拦截策略是根据IP地址增加黑名单。在存在鉴权管理的路由服务中可以通过设置白名单跳过鉴权管理而直接访问后端服务资源。
-
灰度发布:微服务网关可以根据HTTP请求中的特殊标记和后端服务列表元数据标识进行流量控制,实现在用户无感知的情况下完成灰度发布。
-
流量染色:和灰度发布的原理相似,网关可以根据HTTP请求的Host、Head、Agent等标识对请求进行染色,有了网关的流量染色功能,我们可以对服务后续的调用链路进行跟踪,对服务延迟及服务运行状况进行进一步的链路分析。
-
文档中心:网关结合Swagger,可以将后端的微服务暴露给网关,网关作为统一的入口给接口的使用方提供查看后端服务的API规范,不需要知道每一个后端微服务的Swagger地址,这样网关起到了对后端API聚合的效果。
-
日志审计:微服务网关可以作为统一的日志记录和收集器,对服务URL粒度的日志请求信息和响应信息进行拦截。
2、常用网关介绍
2.1 Nginx+Lua
Nginx是一个高性能的HTTP和反向代理服务器。Nginx一方面可以做反向代理,另外一方面可以做静态资源服务器,接口使用Lua动态语言可以完成灵活的定制功能。
-
Nginx适合做门户网关,是作为整个全局的网关,对外的处于最外层的那种;而Gateway属于业务网关,主要用来对应不同的客户端提供服务,用于聚合业务。各个微服务独立部署,职责单一,对外提供服务的时候需要有一个东西把业务聚合起来。
-
Gateway可以实现熔断、重试等功能,这是 Nginx不具备的。
2.2 Kong
Kong是一款基于OpenResty(Nginx + Lua模块)编写的高可用、易扩展的,由Mashape公司开源的API Gateway项目。Kong是基于NGINX和Apache Cassandra或PostgreSQL构建的,能提供易于使用的RESTful API来操作和配置API管理系统,所以它可以水平扩展多个Kong服务器,通过前置的负载均衡配置把请求均匀地分发到各个Server,来应对大批量的网络请求。
-
优点:基于Nginx所以在性能和稳定性上都没有问题。Kong作为一款商业软件,在Ngin上做了很扩展工作,而且还有很多付费的商业插件。Kong本身也有付费的企业版,其中包括技术支持、使用培训服务以及API分析插件。
-
缺点:如果你使用Spring Cloud,Kong如何结合目前已有的服务治理体系?
2.3 Traefik
Traefik 是一个为了让部署微服务更加便捷而诞生的现代HTTP反向代理、负载均衡工具。它支持多种后台 (Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd, Zookeeper, BoltDB, Rest API, file…) 来自动化、动态的应用它的配置文件设置。
- 相对Spring Cloud和 Kubernetes而言,目前比较适合Kubernetes。
2.4 Zuul
Zuul 是 Netflix 开源的一个API网关组件,它可以和 Eureka、Ribbon、Hystrix 等组件配合使用。社区活跃,融合于 SpringCloud 完整生态,是构建微服务体系前置网关服务的最佳选型之一(不过2.0已经闭源)
二、SpringCloud Gateway介绍
1、GateWay模型
官网地址:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
传统的Web框架,比如说: Struts2,SpringMVC等都是基于Servlet APl与Servlet容器基础之上运行的。但是在Servlet3.1之后有了异步非阻塞的支持。而WebFlux是一个典型非阻塞异步的框架,它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上。非阻塞式+函数式编程(Spring 5必须让你使用Java 8)。
Spring WebFlux是Spring 5.0 引入的新的响应式框架,区别于Spring MVC,它不需要依赖Servlet APl,它是完全异步非阻塞的,并且基于Reactor来实现响应式流规范。
2、SPringCloud Gateway特征
-
基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
-
集成断路器
-
集成 Spring Cloud DiscoveryClient
-
Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters
-
具备一些网关的高级功能:动态路由、限流、路径重写
-
路径重写
-
限流
-
动态路由
3、三大核心概念
-
路由(Route)
路由是网关最基础的部分,它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。
-
断言(Predicate)
Java8 中的断言函数。Spring Cloud Gateway 中的断言函数输入类型是 Spring 5.0 框架中 的 ServerWebExchange。Spring Cloud Gateway 中的断言函数允许开发者去定义匹配来自于 Http Request 中的任 何信息,比如请求头和参数等。
-
过滤器(Filter)
一个标准的 Spring Web Filter。Spring Cloud Gateway 中的 Filter 分为两种类型,分别是 Gateway Filter 和 Global Filter。过滤器将会对请求和响应进行处理。
4、工作流程
客户端向Spring Cloud Gateway
发出请求。然后在Gateway Handler Mapping
中找到与请求相匹配的路由,将其发送到GatewayWeb Handler
。Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。Filter在"pre"类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在"post"类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。
- 核心逻辑:路由转发 + 执行过滤器链。
三、环境搭建初体验、
1、简介
这里我注册中心使用了Nacos,需要自行下载并启动,默认端口号是8848,网关使用springcloud gateway。搭建聚合模块demo,一个消费者模块和一个网关模块,请求开源从网关转发到消费者模块。多模块搭建开源参考SpringBoot聚合项目创建、打包与多环境
2、模块搭建
2.1 搭建父模块
创建springboot项目,保留pom.xml
文件
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>cloud-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>cloud-gateway</name>
<description>cloud-gateway</description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<modules>
<module>consumer</module>
<module>gateway</module>
</modules>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.80</version>
</dependency>
</dependencies>
2.2 创建消费者子模块
新建springboot项目,引入依赖,这里注意控制网关和nacos版本,很容易因为版本不对而冲突
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>cloud-gateway</artifactId>
<groupId>com.example</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>consumer</name>
<description>consumer</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.3.12.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- 引入alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
</dependencies>
配置application.yml文件
server:
port: 8888
spring:
application:
name: consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
最后编写简单的controller
@RestController
public class HelloController {
@GetMapping("hello")
public String hello(){
return "world";
}
}
2.3 创建网关子模块
新建springboot项目,引入依赖
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>cloud-gateway</artifactId>
<groupId>com.example</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gateway</name>
<description>gateway</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
<!-- 引入alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
</dependencies>
配置application.yml文件
server:
port: 8889
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: hello
uri: lb://consumer
predicates:
- Path=/** # 断言:路径相匹配的进行路由
nacos:
discovery:
server-addr: localhost:8848
1.3 Cookie
#指定Cookie正则匹配指定值
predicates:
- Cookie=cookie,china
1.4 Header
#指定Header正则匹配指定值,内容必须是数字
predicates:
- Header=X-Request-Id,\d+
# - Header=X-Request-Id
1.5 Host
#请求Host匹配指定值
predicates:
- Host=**.somehost.org,**.anotherhost.org
1.6 Method
#请求Method匹配指定请求方式
predicates:
- Method=GET,POST
1.7 Query
predicates:
#请求包含某参数
- Query=green
#请求包含某参数并且参数值匹配正则表达式
# - Query=red, gree.
1.8 RemoteAddr
#远程地址匹配
predicates:
- RemoteAddr=192.168.1.1/24
1.9 Path
# 断言:路径相匹配的进行路由
predicates:
- Path=/system/**
1.10 Weight
在开发或者测试的时候,或者线上发布,线上服务多版本控制的时候,需要对服务提供权重路由,最常见的使用就是,一个服务有两个版本,旧版本V1,新版本v2。在线上灰度的时候,需要通过网关动态实时推送,路由权重信息。比如80%的流量走服务v1版本,20%的流量走服务v2版本。
predicates: - Weight=group1, 2
2、配置路由的几种方式
2.1 yml配置文件路由
在yml配置文件说明,访问
http://localhost:8889/hello
即可转发#第一种:ws(websocket)方式: uri: ws://localhost:8888 #第二种:http方式: uri: http://localhost:8888/ #第三种:lb(注册中心中服务名字)方式: uri: lb://consumer spring: cloud: gateway: routes: # 路由id,没有固定规则,建议配合服务名 - id: consumer # 匹配后提供服务的路由地址 # 需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能。lb://serviceName是spring cloud gateway在微服务中自动为我们创建的负载均衡uri。 uri: lb://consumer predicates: # 断言:路径相匹配的进行路由 - Path=/**
2.2 通过bean进行配置
注入RouteLocator的Bean
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customerRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
// 第一个参数是路由的唯一id
routes.route("consumer",
r -> r.path("/hello")
.uri("http://localhost:8888/hello")).build();
return routes.build();
}
}
2.3 动态路由
可以通过服务名进行转发,无需配置routes也可以转发,访问http://localhost:8889/consumer/hello
即可转发
spring:
application:
name: gateway
cloud:
gateway:
discovery:
locator:
#开启根据微服务名称自动转发
enabled: true
#小写
lower-case-service-id: true
3、Cloud内置过滤器
3.1 介绍
Spring Cloud Gateway根据作用范围划分为GatewayFilter
和GlobalFilter
,二者区别如下
-
GatewayFilter:网关过滤器,需要通过
spring.cloud.routes.filters
配置在具体路由下,只作用在当前路由上或通过spring.cloud.default-filters
配置在全局,作用在所有路由上。 -
GlobalFilter :全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过
GatewayFilterAdapter
包装成GatewayFilterChain
可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务请求地址的核心过滤器,不需要配置系统初始化时加载,并作用在每个路由上。
3.2 网关过滤器(GatewayFilter)
网关过滤器用于拦截并链式处理 Web 请求,可以实现横切与应用无关的需求,比如:安全、访问超时的设置等。修改传入的 HTTP 请求或传出 HTTP 响应。SpringCloud Gateway 包含许多内置的网关过滤器工厂一共有 22 个,包括头部过滤器、 路径类过滤器、Hystrix 过滤器和重写请求 URL 的过滤器, 还有参数和状态码等其他类型的过滤器。根据过滤器工厂的用途来划分,可以分为以下几种:Header、Parameter、Path、Body、Status、Session、Redirect、Retry、RateLimiter 和Hystrix。
- RewritePath GatewayFilter Factory
RewritePath 网关过滤器工厂采用路径正则表达式参数和替换参数,使用Java 正则表达式来灵活地重写请求路径。下面将http://localhost:8889/hello1/hello
重写为http://localhost:8888/hello
routes:
- id: hello
uri: http://localhost:8888
predicates:
- Path=/hello1/**
- After=2021-02-23T14:20:00.000+08:00[Asia/Shanghai]
filters:
- RewritePath=/hello1/?(?<segment>.*), /$\{segment}
PrefixPath 网关过滤器工厂为匹配的URI添加指定前缀,即在uri路径前加上我们自己的路径然后请求给下游服务
filters:
- PrefixPath=/mypath
- StripPrefix GatewayFilter Factory
StripPrefix网关过滤器工厂采用一个参数 StripPrefix,该参数表示在将请求发送到下游之前从请求中剥离的路径个数,比如下面http://localhost:8889/hello1/test/hello1/hello
请求将变成http://localhost:8888/hello
routes:
- id: hello
uri: http://localhost:8888
predicates:
- Path=/hello1/**
- After=2021-02-23T14:20:00.000+08:00[Asia/Shanghai]
filters:
- StripPrefix=2
- RewritePath=/hello1/?(?<segment>.*), /$\{segment}
- SetPath GatewayFilter Factory
SetPath网关过滤器工厂采用路径模板参数。它提供了一种通过允许模板化路径段来操作请求路径的简单方法,使用了SpringFramework 中的uri模板,允许多个匹配段。下面http://localhost:8889/hello1/hello
请求将变成http://localhost:8888/hello
routes:
- id: hello
uri: http://localhost:8888
predicates:
- Path=/hello1/{segment}
filters:
- SetPath=/{segment}
- AddRequestParameter GatewayFilter Factory
AddRequestParameter网关过滤器工厂会将指定参数添加至匹配到的下游请求中。
#请求添加red=blue给下游
filters:
- AddRequestParameter=red, blue
- SetStatus GatewayFilter Factory
SetStatus网关过滤器工厂采用单个状态参数,它必须是有效的Spring HttpStatus。它可以是整数404或枚举NOT_FOUND的字符串表示。
filters:
#任何情况下,响应的HTTP状态都将设置为401
- SetStatus=401
3.3 全局过滤器
全局过滤器不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter
包装成 GatewayFilterChain
可识别的过滤器,它是请求业务以及路由的 URI 转换为真实业务服务请求地址的核心过滤器,不需要配置系统初始化时加载,并作用在每个路由上。
4、自定义过滤器
4.1 自定义网关过滤器
这里有两种方式配置,一种是实现GatewayFilter
接口,一种是继承AbstractGatewayFilterFactory
,分别作用于路由bean类配置和yml配置上
- 通过bean注册路由
这种自定义网关过滤器需要实现以下两个接口 : GatewayFilter
, Ordered
public class CustomGatewayFilter implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("这是我自定义的局部过滤器");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}}
然后在路由bean类上添加filter,最后访问即可通过我们自己实现的过滤器
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customerRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
// 第一个参数是路由的唯一id
// http://localhost:9527/guonei => http://news.baidu.com/guonei
routes.route("consumer",
r -> r.path("/hello")
.uri("http://localhost:8888/hello")
.filter(new CustomGatewayFilter()))
.build();
return routes.build();
}
}
- yml配置实现路由
这里演示一个黑白名单过滤
首先创建IgnoreWhiteProperties
白名单配置类
//动态刷新类
@RefreshScope
@Configuration
@ConfigurationProperties(prefix = "ignore")
public class IgnoreWhiteProperties
{
/**
* 放行白名单配置,网关不校验此处的白名单
*/
private List<String> whites = new ArrayList<>();
public List<String> getWhites()
{
return whites;
}
public void setWhites(List<String> whites)
{
this.whites = whites;
}
}
配置自定义局部过滤器BlackListUrlFilter
,主要是继承AbstractGatewayFilterFactory
类
@Component
public class BlackListUrlFilter extends AbstractGatewayFilterFactory<BlackListUrlFilter.Config> {
@Autowired
IgnoreWhiteProperties ignoreWhiteProperties;
@Override
public GatewayFilter apply(Config config) {
System.out.println("这是我自定义的局部过滤器");
System.out.println(config);
return (exchange, chain) -> {
String url = exchange.getRequest().getURI().getPath();
// 跳过不需要验证的路径,即白名单
PathMatcher pathMatcher = new AntPathMatcher();
for (String s:ignoreWhiteProperties.getWhites()) {
if (pathMatcher.match(s,url)) {
return chain.filter(exchange);
}
}
if (config.matchBlacklist(url)) {
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return exchange.getResponse().writeWith(
Mono.just(response.bufferFactory().wrap(JSON.toJSONBytes("请求地址不允许访问"))));
}
return chain.filter(exchange);
};
}
public BlackListUrlFilter()
{
super(Config.class);
}
public static class Config {
private List<String> blacklistUrl;private final List<Pattern> blacklistUrlPattern = new ArrayList<>();
public boolean matchBlacklist(String url) {
return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find());
}
public List<String> getBlacklistUrl() {
return blacklistUrl;
}
public void setBlacklistUrl(List<String> blacklistUrl) {
this.blacklistUrl = blacklistUrl;
this.blacklistUrlPattern.clear();
this.blacklistUrl.forEach(url -> {
// 取消正则的贪婪模式
this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE));
});
}
}
}
最后配置一下配置文件
# 不校验白名单
ignore:
whites:
- /hello1/*
- /auth/login
- /*/v2/api-docs
spring:
application:
name: gateway
cloud:
gateway:
discovery:
locator:
#开启根据微服务名称自动转发
enabled: true
lower-case-service-id: true
#注意这里的过滤器有先后顺序
routes:
- id: hello
uri: http://localhost:8888
predicates:
- Path=/hello1/{segment}
filters:
# 也可以简写,但必须要带有xxxGatewayFilterFactory,比如RSAGatewayFilterFactory,只需要写RSA
#- RSA
- SetPath=/{segment}
- name: BlackListUrlFilter
args:
blacklistUrl:
# 黑名单
- /user/list
nacos:
discovery:
server-addr: localhost:8881
5.2 java方式配置
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
最后我们访问sentinel:http://localhost:8080/#/dashboard
,即可在网页设置限流等规则,还可以自定义 API 分组的监控(推荐)
5、网关高可用
一个请求过来,首先经过 Nginx 的一层负载,到达网关,然后由网关负载到真实后端,若后端有问题,网关会进行重试访问,多次访问后仍返回失败,可以通过熔断或服务降级立即返回结果。而且,由于是负载均衡,网关重试时不一定会访问到出错的后端。
参考文章:
微服务系列:服务网关 Spring Cloud Gateway 入门
本文章转载自:https://blog.csdn.net/lemon_TT/article/details/124675243
更多推荐
所有评论(0)