一、网关
在使用微服务时,所有接口直接暴露会导致管理困难。如果像单机架构一样使用过滤器,每个服务都需要单独编写,非常麻烦。针对这个问题,常用的解决方案是使用 API 网关。
1.1 API 网关是什么
API 网关(简称网关)也是一个服务,通常是后端服务的唯一入口。它的定义类似设计模式中的门面模式(Facade Pattern)。它就像整个微服务架构的大门,所有的外部客户端访问都需要经过它来进行调度和过滤。

核心功能:
- 权限控制:作为微服务的入口,对用户进行权限校验,如果校验失败则进行拦截
- 动态路由:一切请求先经过网关,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务
- 负载均衡:当路由的目标服务有多个时,还需要做负载均衡
- 限流:请求流量过高时,按照网关中配置微服务能够接受的流量进行放行,避免服务压力过大
1.2 常见网关实现
业界常用的网关方式有很多,技术方案也较成熟,其中不乏很多开源产品,比如 Nginx,Kong,Zuul,Spring Cloud Gateway 等。下面介绍两种常见的网关方案。
Zuul
Zuul 是 Netflix 公司开源的一个 API 网关组件,是 Spring Cloud Netflix 子项目的核心组件之一,它可以和 Eureka、Ribbon、Hystrix 等组件配合使用。 在 Spring Cloud Finchley 正式版之前,Spring Cloud 推荐的网关是 Netflix 提供的 Zuul(此处指 Zuul 1.X)。 然而 Netflix 在 2018 年宣布一部分组件进入维护状态,不再进行新特性的开发。这部分组件中就包含 Zuul。
Spring Cloud Gateway
Spring Cloud Gateway 是 Spring Cloud 的一个全新的 API 网关项目,基于 Spring + SpringBoot 等技术开发,目的是为了替换掉 Zuul。旨在为微服务架构提供一种简单而有效的途径来转发请求,并为他们提供横切关注点,比如:安全性,监控/指标和弹性。 在性能方面,根据官方提供的测试报告,Spring Cloud Gateway 的 RPS(每秒请求数) 是 Zuul 的 1.6 倍。 https://github.com/spencergibb/spring-cloud-gateway-bench
二、使用 GateWay
基于前面的 Feign 代码开发。
- 引入依赖
创建网关项目

<dependencies>
<!-- 网关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 基于 nacos 实现服务发现依赖 -->
<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>
</dependencies>
- 编写启动类
package com.cloud.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
- 配置文件
server:
port: 10030 # 网关端口
spring:
application:
name: gateway # 服务名称
cloud:
nacos:
discovery:
server-addr: nacos 的 ip 地址 + 端口号
gateway:
routes:
# 网关路由配置
- id: product-service # 路由 ID,自定义,唯一即可
uri: lb://product-service # 目标服务地址
predicates:
# 路由条件
- Path=/product/**
- id: order-service
uri: lb://order-service
predicates:
- Path=/order/**
配置字段说明:
- id:自定义路由 ID,保持唯一
- uri:目标服务地址,支持普通 URI 及 lb://应用注册服务名称。lb 表示负载均衡,使用 lb:// 方式表示从注册中心获取服务地址。
- predicates:路由条件,根据匹配结果决定是否执行该请求路由,上述代码中,我们把符合 Path 规则的一切请求,都代理到 uri 参数指定的地址。


三、Route Predicate Factories
3.1 Predicate
Predicate 是 Java 8 提供的一个函数式编程接口,它接收一个参数并返回一个布尔值,用于条件过滤,请求参数的校验。
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
// ...
}
写一个判断字符串为空的 Predicate 函数:
package predicate;
import java.util.function.Predicate;
/**
* 字符串为空或 null 时返回 true
*/
public class StringPredicate implements Predicate<String> {
@Override
public boolean test(String s) {
return s == null || s.isEmpty();
}
}
使用匿名内部类和 lambda 表达式实现:
package predicate;
import org.junit.Test;
import java.util.function.Predicate;
public class StringPredicateTest {
@Test
public void test() {
StringPredicate stringPredicate = new StringPredicate();
System.out.println(stringPredicate.test("")); // true
System.out.println(stringPredicate.test("123")); // false
System.out.println(stringPredicate.test(null)); // true
}
// 匿名内部类
@Test
public void test2() {
Predicate<String> stringPredicate = new Predicate<String>() {
@Override
public boolean test(String s) {
return s == null || s.isEmpty();
}
};
System.out.println(stringPredicate.test("")); // true
System.out.println(stringPredicate.test("123")); // false
System.out.println(stringPredicate.test(null)); // true
}
// lambda 表达式
@Test
public void test3 {
Predicate<String> stringPredicate = s -> s == || s.isEmpty();
System.out.println(stringPredicate.test());
System.out.println(stringPredicate.test());
System.out.println(stringPredicate.test());
}
}

其他方法:
- isEqual(Object targetRef) : 比较两个对象是否相等,参数可以为 Null
- and(Predicate other):短路与操作,返回一个组成 Predicate
- or(Predicate other):短路或操作,返回一个组成 Predicate
- test(T t):传入一个 Predicate 参数,用来做判断
- negate():返回表示此 Predicate 逻辑否定的 Predicate
| 方法 | 说明 |
|---|---|
| boolean test(T t) | 判断条件,可以理解为 条件 A 根据逻辑返回布尔值 |
| Predicate and(Predicate<? super T> other) | 条件 A && 条件 B 当前 Predicate 的 test 方法 && other 的 test 方法,相当于进行两次判断 |
| default Predicate negate() | ! 条件 A |
| default Predicate or(Predicate<? super T> other) | 条件 A |
3.2 Route Predicate Factories
Route Predicate Factories (路由断言工厂,也称为路由谓词工厂),在 Spring Cloud Gateway 中,Predicate 提供了路由规则的匹配机制。 我们在配置文件中写的断言规则只是字符串,这些字符串会被 Route Predicate Factory 读取并处理,转变为路由判断的条件。
这个规则是由 org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory 来实现的。
Spring Cloud Gateway 默认提供了很多 Route Predicate Factory,这些 Predicate 会分别匹配 HTTP 请求的不同属性,并且多个 Predicate 可以通过 and 逻辑进行组合。
| 名称 | 说明 | 示例 |
|---|---|---|
| After | 这个工厂需要一个日期时间 (Java 的 ZonedDateTime 对象),匹配指定日期之后的请求 | predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver] |
| Before | 匹配指定日期之前的请求 | predicates: - Before=2017-01-20T17:42:47.789-07:00[America/Denver] |
| Between | 匹配两个指定时间之间的请求 datetime2 的参数必须在 datetime1 之后 | predicates: - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver] |
| Cookie | 请求中包含指定 Cookie,且该 Cookie 值符合指定的正则表达式 | predicates: - Cookie=chocolate, ch.p |
| Header | 请求中包含指定 Header,且该 Header 值符合指定的正则表达式 | predicates: - Header=X-Request-Id, \d+ |
| Host | 请求必须是访问某个 host(根据请求中的 Host 字段进行匹配) | predicates: -Host=**.somehost.org,**.anotherhost.org |
| Method | 匹配指定的请求方式 | predicates: - Method=GET,POST |
| Path | 匹配指定规则的路径 | predicates: - Path=/red/{segment},/blue/{segment} |
| Remote Addr | 请求者的 IP 必须为指定范围 | predicates: - RemoteAddr=192.168.1.1/24 |
3.3 Gateway Filter Factories(网关过滤器工厂)
Predicate 决定了请求由哪一个路由处理,如果在请求处理前后需要加一些逻辑,这就是 Filter(过滤器) 的作用范围了。 Filter 分为两种类型:Pre 类型和 Post 类型。
- Pre 类型过滤器:路由处理之前执行 (请求转发到后端服务之前执行),在 Pre 类型过滤器中可以做鉴权,限流等。
- Post 类型过滤器:请求执行完成后,将结果返回给客户端之前执行。

Spring Cloud Gateway 中内置了很多 Filter,用于拦截和链式处理 web 请求。比如权限校验,访问超时等设定。 Spring Cloud Gateway 从作用范围上,把 Filter 可分为 GatewayFilter 和 GlobalFilter。
- GatewayFilter:应用到单个路由或者一个分组的路由上。
- GlobalFilter:应用到所有的路由上,也就是对所有的请求生效。
3.3.1 GatewayFilter
GatewayFilter 同 Predicate 类似,都是在配置文件 application.yml 中配置,每个过滤器的逻辑都是固定的。比如 AddRequestParameterGatewayFilterFactory 只需要在配置文件中写 AddRequestParameter,就可以为所有的请求添加一个参数。
| 名称 | 说明 | 示例 |
|---|---|---|
| AddRequestHeader | 为当前请求添加 Header 参数:Header 的名称及值 | -AddRequestHeader=X-Request-red,blue |
| AddRequestParameter | 为当前请求添加请求参数 参数:参数的名称及值 | -AddRequestParameter=red,blue |
| AddResponseHeader | 为响应结果添加 Header 参数:Header 的名称及值 | -AddResponseHeader=X-Response-Red,Blue |
| RemoveRequestHeader | 从当前请求删除某个 Header 参数:Header 的名称 | -RemoveRequestHeader=X-Request-Foo |
| RemoveResponseHeader | 从响应结果删除某个 Header 参数:Header 的名称 | -RemoveResponseHeader=X-Response-Foo |
| RequestRateLimiter | 为当前网关的所有请求执行限流过滤,若被限流默认返回 HTTP 429 Too Many Requests。 默认提供 RedisRateLimiter 的限流实现,采用令牌桶算法: - redis-rate-limiter.replenishRate:令牌填充速度,即每秒钟允许多少个请求(不丢弃任何请求) - redis-rate-limiter.burstCapacity:令牌桶容量,即每秒用户最大能够执行的请求数量(不丢弃任何请求),设为 0 将阻止所有请求 - redis-rate-limiter.requestedTokens:每次请求占用几个令牌,默认为 1 | filters:- name: RequestRateLimiterargs:redis-rate-limiter.replenishRate: 10redis-rate-limiter.burstCapacity: 20redis-rate-limiter.requestedTokens: 1 |
| Retry | 针对不同响应进行重试。当后端服务不可用时,网关根据配置参数发起重试请求。 - retries:重试次数,默认为 3 - statuses:HTTP 请求返回的状态码,针对指定状态码进行重试,对应 org.springframework.http.HttpStatus | filters:- name: Retryargs:retries: 3statuses: BAD_REQUEST |
| RequestSize | 设置允许接收的最大请求包大小,若请求包大小超过设定值,则返回 413 Payload Too Large。 请求包大小单位为字节,默认值为 5 MB。 | filters:- name: RequestSizeargs:maxSize: 5000000 |
| 默认过滤器 | 添加一个 filter 并将其应用于所有路由,该属性需要一个 filter 列表,对所有路由都生效。 | spring: cloud: gateway: default-filters: |
3.3.2 GlobalFilter
GlobalFilter 是 Spring Cloud Gateway 中的全局过滤器,它和 GatewayFilter 的作用是相同的。GlobalFilter 会应用到所有的路由请求上,全局过滤器通常用于实现与安全性,性能监控和日志记录等相关的全局功能。 Spring Cloud Gateway 内置的全局过滤器也有很多,比如:
- Gateway Metrics Filter:网关指标,提供监控指标
- Forward Routing Filter:用于本地 forword,请求不转发到下游服务器
- LoadBalancer Client Filter:针对下游服务,实现负载均衡。
3.4 过滤器执行顺序
一个项目中,既有 GatewayFilter,又有 GlobalFilter 时,执行的先后顺序是: 请求路由后,网关会把当前项目中的 GatewayFilter 和 GlobalFilter 合并到一个过滤器链 (集合) 中,并进行排序,依次执行过滤器。

每一个过滤器都必须指定一个 int 类型的 order 值,默认值为 0,表示该过滤的优先级。order 值越小,优先级越高,执行顺序越靠前。Filter 通过实现 Order 接口或者添加@Order 注解来指定 order 值。Spring Cloud Gateway 提供的 Filter 由 Spring 指定。用户也可以自定义 Filter,由用户指定。当过滤器的 order 值一样时,会按照 defaultFilter > GatewayFilter > GlobalFilter 的顺序执行。
3.5 自定义过滤器
Spring Cloud Gateway 提供了过滤器的扩展功能,开发者可以根据实际业务来定义过滤器,同样自定义过滤器也支持 GatewayFilter 和 GlobalFilter 两种。
3.5.1 自定义 GatewayFilter
自定义 GatewayFilter,需要去实现对应的接口 GatewayFilterFactory,Spring Boot 默认帮我们实现的抽象类是 AbstractGatewayFilterFactory,我们可以直接使用。 我们在前面配置文件中使用过滤器的时候会传参数,我们需要自定义类来接收参数。
package com.cloud.gateway;
import lombok.Data;
@Data
public class CustomConfig {
private String name;
}
自定义过滤器:
- 类名统一以 GatewayFilterFactory 结尾,因为默认情况下,过滤器的 name 会采用该定义类的前缀。这里的 name=Custom(yml 配置中使用)
- apply 方法中,同时包含 Pre 和 Post 过滤,then 方法中是请求执行结束之后处理的
- CustomConfig 是一个配置类,该类只有一个属性 name,和 yml 的配置对应
- 该类需要交给 Spring 管理,所以需要加 @Component 注解
- getOrder 表示该过滤器的优先级,值越大,优先级越低。
package com.cloud.gateway;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomConfig> implements Ordered {
public CustomGatewayFilterFactory() {
super(CustomConfig.class);
}
/**
* Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
* ServerWebExchange: HTTP 请求 - 响应交互的契约,提供对 HTTP 请求和响应的访问,服务器端请求属性,请求实例,响应实例等,类似 Context 角色
* GatewayFilterChain: 过滤器链
* Mono: Reactor 核心类,数据流发布者,Mono 最多只触发一个事件,所以可以把 Mono 用于在异步任务完成时发出通知.
* Mono.fromRunnable: 创建一个包含 Runnable 元素的数据流
*/
@Override
public GatewayFilter apply(CustomConfig config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("[Pre] Filter Request, name:" + config.getName());
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("[Post] Response Filter");
}));
}
};
}
{
Ordered.LOWEST_PRECEDENCE;
}
}
配置:
server:
port: 10030 # 网关端口
spring:
application:
name: gateway # 服务名称
cloud:
gateway:
routes:
# 网关路由配置
- id: product-service # 路由 ID,自定义,唯一即可
uri: lb://product-service # 目标服务地址
predicates:
# 路由条件
- Path=/product/**
filters:
- name: Custom
args:
name: custom filter

3.5.2 自定义 GlobalFilter
GlobalFilter 的实现比较简单,它不需要额外的配置,只需要实现 GlobalFilter 接口,自动会过滤所有的 Filter。
package com.cloud.gateway;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("[Pre] CustomGlobalFilter enter...");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("[Post] CustomGlobalFilter return...");
}));
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}



