目录
- 一、网关
-
- [1.1 API网关是什么](#1.1 API网关是什么)
- [1.2 常见网关实现](#1.2 常见网关实现)
- 二、使用GateWay
- [三、Route Predicate Factories](#三、Route Predicate Factories)
-
- [3.1 Predicate](#3.1 Predicate)
- [3.2 Route Predicate Factories](#3.2 Route Predicate Factories)
- [3.3 Gateway Filter Factories(⽹关过滤器⼯⼚)](#3.3 Gateway Filter Factories(⽹关过滤器⼯⼚))
-
- [3.3.1 GatewayFilter](#3.3.1 GatewayFilter)
- [3.3.2 GlobalFilter](#3.3.2 GlobalFilter)
- [3.4 过滤器执⾏顺序](#3.4 过滤器执⾏顺序)
- [3.5 ⾃定义过滤器](#3.5 ⾃定义过滤器)
-
- [3.5.1 自定义GatewayFilter](#3.5.1 自定义GatewayFilter)
- [3.5.2 自定义GlobalFilter](#3.5.2 自定义GlobalFilter)

一、网关
我们在使用微服务的时候,所有接口都是直接暴露的,如果像原来单机架构一样,使用过滤器就需要每一个服务都去写,非常麻烦。针对这个问题,⼀个常⽤的解决⽅案是使⽤API⽹关。
1.1 API网关是什么
API⽹关(简称⽹关)也是⼀个服务,通常是后端服务的唯⼀⼊⼝。它的定义类似设计模式中的Facade模式(⻔⾯模式,也称外观模式)。它就类似整个微服务架构的⻔⾯,所有的外部客⼾端访问,都需要经过它来进⾏调度和过滤。相当于大门。

核心功能:
- 权限控制:作为微服务的⼊⼝,对⽤⼾进⾏权限校验,如果校验失败则进⾏拦截
- 动态路由:⼀切请求先经过⽹关,但⽹关不处理业务,⽽是根据某种规则,把请求转发到某个微服务
- 负载均衡:当路由的⽬标服务有多个时,还需要做负载均衡
- 限流:请求流量过⾼时,按照⽹关中配置微服务能够接受的流量进⾏放⾏,避免服务压⼒过⼤。
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的代码开发。
-
创建网关项目

-
引入依赖
xml
<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>
- 编写启动类
java
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);
}
}
- 配置文件
java
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提供的⼀个函数式编程接⼝,它接收⼀个参数并返回⼀个布尔值,⽤于条件过滤,请求参数的校验。
java
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
//...
}
写一个判断字符串为空的Predicate函数:
java
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表达式实现:
java
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 == null || s.isEmpty();
System.out.println(stringPredicate.test("")); // true
System.out.println(stringPredicate.test("123")); // false
System.out.println(stringPredicate.test(null)); // true
}
}

其他方法:
- 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: RequestRateLimiter args: redis-rate-limiter.replenishRate: 10 redis-rate-limiter.burstCapacity: 20 redis-rate-limiter.requestedTokens: 1 |
| Retry | 针对不同响应进行重试。当后端服务不可用时,网关根据配置参数发起重试请求。 - retries:重试次数,默认为 3 - statuses:HTTP 请求返回的状态码,针对指定状态码进行重试,对应 org.springframework.http.HttpStatus | filters: - name: Retry args: retries: 3 statuses: BAD_REQUEST |
| RequestSize | 设置允许接收的最大请求包大小,若请求包大小超过设定值,则返回 413 Payload Too Large。 请求包大小单位为字节,默认值为 5 MB。 | filters: - name: RequestSize args: 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 ,我们可以直接使⽤。
我们在前面配置文件中使用过滤器的时候会传参数,我们需要自定义类来接收参数。
java
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表⽰该过滤器的优先级,值越⼤,优先级越低。
java
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");
}));
}
};
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
配置:
yml
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。
java
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;
}
}
