本文主要总结Spring Gateway的基础用法,内容包括网关、Spring Gateway工作流程、Spring Cloud Gateway搭建、路由配置方式、负载均衡实现、断言工厂这几个部分
目录
在微服务架构中,一个系统会被拆分为多个微服务,那么作为客户端如何去调用这些微服务呢?如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去调用,这样会产生很多问题:
为了解决上述问题引入了网关概念:所谓的API网关就是指系统的统一入口,提供内部服务的路由中转,为客户端提供统一的服务,一些与业务本身功能无关的公共逻辑可以在这里实现,例如认证、鉴权、监控、路由转发等。
Zuul、Nginx+Lua 和 Spring Cloud Gateway 都可以用作API网关,但它们在不同方面有一些不同之处,包括性能、扩展性、功能和用途。以下是它们的主要比较:
Zuul: 2. 性能:Zuul性能较低,特别是在处理大量并发请求时。它使用的是阻塞I/O,每个请求都会占用一个线程。 3. 扩展性:Zuul的扩展性相对较低。定制功能需要使用过滤器,这可能不够灵活。 4. 功能:Zuul是Netflix OSS的一部分,提供基本的路由、负载均衡、重试等功能。然而,它的功能相对有限。 5. 用途:适合小规模的、非高并发的应用,或者作为微服务架构中的较早版本API网关。
Nginx+Lua: 2. 性能:Nginx以其出色的性能而闻名。Nginx+Lua的组合可以在处理高并发请求时提供高性能,特别适合于需要低延迟的应用。 3. 扩展性:Nginx+Lua提供了强大的扩展性。使用Lua脚本,你可以定制和扩展其功能以适应各种需求。 4. 功能:Nginx是一个功能强大的反向代理服务器,可以处理负载均衡、缓存、SSL终端、请求重写、安全性等各种功能。 5. 用途:适合高并发的生产环境,特别是需要高性能和低延迟的应用,一般用于一层网关。
Spring Cloud Gateway: 2. 性能:Spring Cloud Gateway在性能方面表现不错,它使用基于反应性的编程模型,充分利用非阻塞I/O。 3. 扩展性:Spring Cloud Gateway提供了很好的扩展性,它是基于Spring Framework构建的,因此可以与Spring生态系统的其他组件无缝集成。 4. 功能:Spring Cloud Gateway提供了路由、过滤器、限流、熔断等功能,可以满足大多数API网关的需求。 5. 用途:适合构建微服务架构中的API网关,特别是在使用Spring Boot和Spring Cloud的应用中。
Spring Cloud Gateway是一个用于构建API网关的开源项目,它是Spring Cloud生态系统的一部分。API网关是微服务架构中的关键组件之一,用于管理和路由传入的请求,提供了一种集中化的访问点,可以实现诸如路由、负载均衡、身份验证、安全性、监控、限流、重试等功能。Spring Cloud Gateway提供了一种现代、动态、灵活且功能强大的方式来处理这些任务。
以下是Spring Cloud Gateway的一些主要特点和功能:
动态路由:Spring Cloud Gateway允许你根据需要动态配置路由,这意味着你可以在不停止网关的情况下修改路由规则。路由配置通常存储在配置中心(如Spring Cloud Config)中,从而实现灵活的路由管理。
过滤器:Gateway提供了一组预定义的过滤器,用于请求和响应的修改、校验、验证和监控。你还可以编写自定义过滤器以适应特定需求。
负载均衡:Gateway集成了Spring Cloud LoadBalancer,可实现请求的负载均衡。你可以将请求路由到多个后端服务实例,实现高可用和扩展性。
限流和熔断:Spring Cloud Gateway支持通过集成Hystrix来实现限流和熔断功能,以保护后端服务免受过多的请求压力。
安全性:可以与Spring Security或其他身份验证和授权机制集成,实现安全的API访问。
监控和指标:Spring Cloud Gateway集成了Spring Boot Actuator,提供了各种有用的监控和度量信息,可以使用Micrometer将这些信息导出到多个监控系统。
WebSockets支持:Gateway支持WebSockets,因此可以处理实时通信的需求。
HTTP/2支持:可以通过配置启用HTTP/2协议,以提高性能。
自定义路由规则:除了使用配置外,你还可以编写自定义的路由规则,以实现更复杂的路由需求。
Spring Cloud Gateway是建立在Spring Framework和Spring Boot的基础上的,因此与Spring生态系统的其他组件无缝集成。它为构建微服务架构中的API网关提供了灵活、高性能和现代的解决方案。
在Spring Gateway中:
路由(Route):路由是网关的基本构建单元,包括以下属性:
举例:定义一个路由,将匹配/example
路径的请求,将其转发到http://example.com
:
断言(Predicate):谓词是用于匹配HTTP请求的条件。谓词可以基于请求的各种属性,如路径、主机、标头等。例如,Path
谓词可以匹配请求路径。
举例:定义一个谓词,要求请求路径必须以/api
开头:
predicates:
过滤器(Filter):过滤器是网关中的处理器,它们可以在请求到达目标URI之前或响应返回之前对请求和响应进行修改。过滤器可以用于添加标头、修改请求体、记录日志等操作。
举例:定义一个过滤器,向响应标头中添加X-Custom-Header
标头:
filters:
这些组件一起构成了Spring Gateway的核心功能,允许您定义路由规则、请求匹配条件以及请求和响应的处理操作。
SpringCloud Gateway使用的是WebFlux中的reactor-netty响应式编程组件,底层使用Netty通讯框架。
核心流程图如下:
如上图所示,当使用Spring Gateway处理请求时,它经历以下流程:
Client向Gateway Server发送请求: 客户端(例如浏览器、移动应用或其他服务)发送HTTP请求到Spring Gateway服务器,这是请求的起始点。
请求会被HttpWebHandlerAdapter提取组装成网关上下文:
请求进入Spring Gateway后,首先被HttpWebHandlerAdapter处理。这个组件负责提取HTTP请求信息,并将其组装成网关上下文对象,其中包括请求的各种信息,如HTTP头、请求参数等。
网关上下文会传递到DispatcherHandler:
组装好的网关上下文被传递到DispatcherHandler,它是Spring Gateway的核心处理器。DispatcherHandler的任务是将请求分发到合适的处理程序。
RoutePredicateHandlerMapping负责路由查找,并根据路由断言判断路由是否可用:
RoutePredicateHandlerMapping负责路由查找,它将根据网关上下文中的信息选择适当的路由。每个路由都包括一个或多个路由断言,这些断言是用来判断该路由是否适用于当前请求的条件。
如果断言成功,由FilteringWebHandler创建过滤器链并调用:
如果路由断言成功,表示当前请求匹配了某个路由规则。此时,将会由FilteringWebHandler创建一个过滤器链,并按照链的顺序调用这些过滤器。过滤器可以在请求到达后端服务之前或响应返回给客户端之前对请求进行修改、验证或记录日志等操作。
这个流程允许Spring Gateway进行请求的路由和过滤,以实现对请求的控制和处理。通过配置不同的路由规则和过滤器,可以根据请求的性质来决定如何处理请求,例如路由到不同的后端服务、添加安全性措施或改变请求和响应的内容。
Gateway的核心流程就是:路由转发+执行过滤器链
搭建一个cloud-alibaba-gateway项目,因为使用了spring cloud 所以需要注意版本间的匹配,这里可以通过阿里的云原生脚手架去获取适合的版本:[start.aliyun.com/]
搭建Spring Gateway项目之前需要一些前置搭建微服务注册中心(Nacos,Consul,Eureka等),这里我使用Nacos进行演示,Nacos的搭建流程可以参考:[juejin.cn/post/705471…]
该案例的完整代码可以在gitee项目中获取:[gitee.com/lei-qinghua…]
- <?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>
- <groupId>com.ts</groupId>
- <artifactId>cloud-alibaba-gateway</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <name>cloud-alibaba-gateway</name>
-
- <properties>
- <java.version>1.8</java.version>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- <spring-boot.version>2.6.13</spring-boot.version>
- <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
- <spring-cloud.version>2021.0.5</spring-cloud.version>
- </properties>
-
- <dependencies>
- <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</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-gateway</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-dependencies</artifactId>
- <version>${spring-cloud.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-dependencies</artifactId>
- <version>${spring-boot.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-alibaba-dependencies</artifactId>
- <version>${spring-cloud-alibaba.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>3.8.1</version>
- <configuration>
- <source>1.8</source>
- <target>1.8</target>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- <version>${spring-boot.version}</version>
- <configuration>
- <mainClass>com.ts.demo.DemoApplication</mainClass>
- <skip>true</skip>
- </configuration>
- <executions>
- <execution>
- <id>repackage</id>
- <goals>
- <goal>repackage</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- </project>
注意:引入Gateway一定要删除spring-boot-starter-web依赖,否则会发生冲突无法引入。
- server:
- port: 9999
-
- spring:
- application:
- name: cloud-gateway-service
- cloud:
- nacos:
- discovery:
- server-addr: localhost:8848
- gateway:
- discovery:
- locator:
- enabled: true # 开启了spring gateway
- routes:
- - id: nacos-provider #服务id,对应nacos中注册的服务名
- uri: http://localhost:9003/nacos-provider #访问的uri地址
- predicates:
- - Path=/ts/** #断言,对请求进行匹配,只有包含/ts的路径才能通过断言
-
controller (微服务cloud-alibaba-provider-9003)
这里我们搭建了另外一个微服务cloud-alibaba-provider-9003作为服务的提供者来验证网关是否生效,这个微服务的逻辑非常简单,使用HashMap模拟一个一个数据库的获取操作如下:
- @RequestMapping("/ts")
- @RestController
- public class DataController {
- @Value("${server.port}")
- String serverPort;
-
- public static HashMap
hashMap = new HashMap<>(); - static {
- hashMap.put(1l,"鼠标");
- hashMap.put(2l,"键盘");
- hashMap.put(3l,"耳机");
- }
-
- @GetMapping("info/{id}")
- public JsonResult
msbSQL(@PathVariable("id") Long id) { - JsonResult
result = new JsonResult<>(200,"ServerPort: "+serverPort+":"+hashMap.get(id)); - return result;
- }
-
- @GetMapping("/timeout")
- public JsonResult
timeout(){ - try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- return new JsonResult<>(200,"ServerPort: "+serverPort);
- }
- }
测试:[http://localhost:9999/ts/timeout]
如下图所示,我们通过微服务提供者的url对服务进行访问,而是通过gateway网关的路径对微服务进行访问成功了,就说明我们的网关搭建成功了。
这里以一个简单的例子展示了Spring Gateway网关的基础搭建和测试过程,相关的服务搭建都是在本地完成的,完整的代码请参考我的gitee项目:[gitee.com/lei-qinghua…]
在第三节中我们用yml文件对Gateway的路由方式进行了配置,实际上Gateway还可以通过Java代码的方式来进行路由配置。
通过@Bean 注入一个RouteLocator,代码如下:
- package com.example.cloudalibabagateway.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 {
- @Bean
- public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
- RouteLocatorBuilder.Builder routes =routeLocatorBuilder.routes();
- routes.route("path_ts",r -> r.path("/ts/**").uri("http://localhost:9003/nacos-provider"));
- return routes.build();
- }
- }
- server:
- port: 9999
-
- spring:
- application:
- name: cloud-gateway-service
- cloud:
- nacos:
- discovery:
- server-addr: localhost:8848
- gateway:
- discovery:
- locator:
- enabled: true
- routes:
- - id: nacos-provider
- uri: http://localhost:9003/nacos-provider
- predicates:
- - Path=/ts/**
微服务系统的负载均衡是一种机制,用于在微服务架构中均匀分配网络请求和流量到不同的微服务实例,以确保各个服务的资源充分利用,提高系统的性能、可用性和稳定性。在微服务架构中,通常存在多个相同或相似的微服务实例,每个实例都提供相同的服务接口,但可能运行在不同的主机或容器上。
以下是微服务系统中负载均衡的一些关键概念和特点:
服务实例均衡分配:微服务负载均衡确保来自客户端的请求均匀分布到不同的服务实例上,以防止某些实例过载而其他实例处于空闲状态。
多种负载均衡策略:微服务负载均衡可以采用不同的策略,如轮询、随机、最少连接等,以选择目标服务实例。每种策略有其用途,根据具体情况选择合适的策略。
健康检查:负载均衡器定期检查每个服务实例的健康状态,以确定哪些实例可以接收请求。如果某个实例不健康或不可用,负载均衡器将停止将请求路由到该实例。
自动扩展:微服务系统的负载均衡应该支持自动扩展。当系统负载增加时,可以自动添加新的服务实例以处理更多请求。这有助于应对流量波动和系统的横向扩展。
故障转移:如果某个服务实例出现故障,负载均衡器应该自动将流量转移到其他健康的实例,以确保系统的可用性。
会话粘附:在某些情况下,需要确保同一客户端的多个请求被路由到同一服务实例,以维护会话一致性。这称为会话粘附,一些负载均衡器支持此功能。
微服务系统的负载均衡是确保整个系统运行顺畅的重要组成部分。它有助于避免单点故障,提高系统的可用性,并允许系统根据需求自动扩展。选择适当的负载均衡策略和工具对于构建稳健的微服务系统至关重要。
Gateway还提供了和Zuul类似的自动路由规则,具体配置如下:
- server:
- port: 9999
-
- spring:
- application:
- name: cloud-gateway-service
- cloud:
- nacos:
- discovery:
- server-addr: localhost:8848
- gateway:
- discovery:
- locator:
- enabled: true # 开启自动路由功能,根据服务名自动创建routes
测试:[http://localhost:9999/nacos-provider/info/1]
我们直接通过spring gateway的服务地址和服务提供者nacos-provider的服务名组成的url地址,就可以以负载均衡的方式访问nacos-provider的服务。
自动负载均衡存在一个问题就是需要暴露每一个服务的服务名称,因此可以采用手动负载均衡的方式来避免暴露微服务的服务名称。
- server:
- port: 9999
-
- spring:
- application:
- name: cloud-gateway-service
- cloud:
- nacos:
- discovery:
- server-addr: localhost:8848
- gateway:
- discovery:
- locator:
- enabled: true
- routes:
- - id: nacos-provider
- uri: lb://nacos-provider # 开启负载均衡,服务名称为nacos-provider
- predicates: # 断言,放开所有路径
- - Path=/**
Gateway断言可以理解为当满足条件后才会进行转发,总结就是Predicate就是为了实现一组匹配规则,让请求过来找到相应的Route进行处理。
Spring Cloud Gateway支持多种路由断言(Route Predicates)类型,用于匹配和路由HTTP请求。以下是一些常见的路由断言类型:
predicates:
这将匹配所有以/api/
开头的路径。
predicates:
这将匹配主机名为example.com
的请求。
predicates:
这将匹配HTTP GET请求。
predicates:
这将匹配包含Authorization
头且值以Bearer开头的请求。
predicates:
这将匹配包含name=john
的查询参数的请求。
predicates:
这将匹配包含名为sessionId
的Cookie的请求。
predicates:
这将匹配主机为example.com
且路径以/api/
开头的请求。
这些路由断言允许你根据请求的不同属性(如路径、主机、请求头、HTTP方法等)进行匹配和路由,从而更灵活地控制请求的路由和处理。你可以在Spring Cloud Gateway的路由配置中定义这些路由断言,以满足你的具体需求。
路由过滤器允许以某种方式修改传入的http请求或传出的HTTP响应,路由过滤器的范围是特定的路由Spring Cloud Gateway包含许多内置的Gateway Filter工厂。
Gateway内置的Filter生命周期分为两种:pre(业务逻辑之前)、post(业务逻辑之后)
Gateway本身自带的Filter分为2种:GateWayFilter(单一)、GlobalFilter(全局)
Spring Cloud Gateway提供了多个内置的过滤器(Filters),这些过滤器允许你在请求和响应的生命周期中执行各种操作,例如请求路由、修改请求和响应、增加头信息等。以下是一些常见的内置过滤器:
Forward Routing Filter:用于路由请求到后端服务。
LoadBalancerClient Filter:通过LoadBalancerClient执行负载均衡请求。
AddRequestHeader Filter:在请求中添加头信息。
AddRequestParameter Filter:在请求中添加查询参数。
RewritePath Filter:重写请求路径,用于修改请求的路径。
SetStatus Filter:设置HTTP响应状态码。
AddResponseHeader Filter:在响应中添加头信息。
Hystrix Filter:用于Hystrix断路器的支持。
WebSockets Filter:用于WebSocket代理。
ModifyResponseBody Filter:修改响应体内容。
PreserveHostHeader Filter:保留原始主机头信息。
RequestRateLimiter Filter:实现请求速率限制。
这些内置过滤器可以通过Spring Cloud Gateway的路由配置文件进行配置,从而对请求和响应进行自定义处理。你可以根据具体需求组合和配置这些过滤器,以满足你的应用程序的需求。此外,你也可以编写自定义过滤器来执行更高度定制的操作。过滤器在Gateway中扮演了非常重要的角色,帮助你实现请求的路由和处理逻辑。
Spring Gateway提供了许多内置的过滤器,用于执行常见的网关任务,例如鉴权、请求转发、重定向等。你可以在Spring Gateway的配置中添加这些内置过滤器来满足你的需求。以下是如何配置内置过滤器的一些示例:
AddRequestHeader
过滤器来添加认证信息到请求头中。- spring:
- cloud:
- gateway:
- default-filters:
- - name: AddRequestHeader
- args:
- X-Request-Auth: some-auth-token
-
3. 重定向过滤器:用于将请求重定向到其他路径或URL。你可以使用RedirectTo
过滤器来执行重定向操作。
- spring:
- cloud:
- gateway:
- routes:
- - id: my_redirect_route
- uri: http://example.com
- predicates:
- - Path=/redirect
- filters:
- - RedirectTo=302:http://new-location.com
-
4. 请求转发过滤器:用于将请求转发到其他服务或路径。你可以使用ForwardTo
过滤器来执行请求转发。
- spring:
- cloud:
- gateway:
- routes:
- - id: my_forward_route
- uri: http://example.com
- predicates:
- - Path=/forward
- filters:
- - ForwardTo=http://forward-service.com
-
5. 添加响应头过滤器:用于在响应中添加额外的头信息。你可以使用AddResponseHeader
过滤器来添加响应头。
- spring:
- cloud:
- gateway:
- default-filters:
- - name: AddResponseHeader
- args:
- X-Response-Header: some-value
-
这只是一些内置过滤器的示例,Spring Gateway提供了更多的内置过滤器,你可以根据你的需求在配置中使用它们。通过合理配置内置过滤器,你可以实现许多常见的网关功能,而无需自行编写复杂的逻辑。
要创建自定义过滤器(Custom Filter)来扩展Spring Cloud Gateway的功能,你需要遵循一些步骤。自定义过滤器可以用于执行各种自定义操作,例如鉴权、日志记录、修改请求和响应等。以下是创建自定义过滤器的一般步骤:
创建一个自定义过滤器类:首先,你需要创建一个Java类,实现GatewayFilter
或GlobalFilter
接口。这两个接口分别用于创建局部过滤器和全局过滤器。
局部过滤器 (GatewayFilter
) 会应用于特定路由的请求。
全局过滤器 (GlobalFilter
) 会应用于所有路由的请求。
下面我们以创建一个全局过滤器来展示如何创建自定义过滤器,要创建自定义的全局过滤器,你需要实现Spring Cloud Gateway的 GlobalFilter
接口。以下是创建一个简单的全局自定义过滤器的步骤:
创建一个类,实现 GlobalFilter
接口。
在该类上添加 @Component
注解,以便Spring容器可以扫描并管理它。
实现 filter
方法,该方法包含你自定义过滤器的逻辑。
以下是一个示例自定义全局过滤器的代码:
- import org.springframework.cloud.gateway.filter.GlobalFilter;
- import org.springframework.core.Ordered;
- import org.springframework.core.annotation.Order;
- import org.springframework.core.io.buffer.DataBuffer;
- import org.springframework.http.server.reactive.ServerHttpRequest;
- import org.springframework.http.server.reactive.ServerHttpResponse;
- import org.springframework.stereotype.Component;
- import org.springframework.web.server.ServerWebExchange;
- import reactor.core.publisher.Mono;
-
- @Component
- public class CustomGlobalFilter implements GlobalFilter, Ordered {
-
- @Override
- public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
- // 在请求处理之前执行自定义逻辑
- ServerHttpRequest request = exchange.getRequest();
- ServerHttpResponse response = exchange.getResponse();
-
- // 在响应中添加自定义的HTTP头
- response.getHeaders().add("X-Custom-Header", "CustomHeaderValue");
-
- // 修改请求或响应内容
- ServerHttpRequest modifiedRequest = request.mutate()
- .header("X-Modified-Header", "ModifiedValue")
- .build();
- ServerWebExchange modifiedExchange = exchange.mutate()
- .request(modifiedRequest)
- .build();
-
- // 执行链中的下一个过滤器或处理器
- return chain.filter(modifiedExchange).then(Mono.fromRunnable(() -> {
- // 在请求处理完成后执行自定义逻辑
- // 这里可以对响应进行进一步处理
- }));
- }
-
- @Override
- public int getOrder() {
- // 指定过滤器的执行顺序,可以是负数、零、正数,数字越小,执行顺序越靠前
- return Ordered.HIGHEST_PRECEDENCE;
- }
- }
在上面的示例中,CustomGlobalFilter
是一个全局过滤器,它在请求处理前和请求处理后执行自定义逻辑。你可以在 filter
方法中访问请求、响应,修改它们的内容,添加自定义HTTP头,以及执行其他自定义逻辑。getOrder
方法用于指定过滤器的执行顺序,数字越小,执行顺序越靠前。
确保将 CustomGlobalFilter
类放在Spring Boot应用程序的类路径下,Spring会自动识别并应用它。这样,你的自定义全局过滤器就会在请求到达Spring Cloud Gateway时生效。