共计 5457 个字符,预计需要花费 14 分钟才能阅读完成。
什么是网关
在微服务架构里,服务的粒度被进一步细分,各个业务服务可以被独立的设计、开发、测试、部署和管理。这时,各个独立部署单元可以用不同的开发测试团队维护,可以使用不同的编程语言和技术平台进行设计,这就要求必须使用一种语言和平台无关的服务协议作为各个单元间的通讯方式。
换句话说就是网关为所有的请求提供了统一的入口,方便我们对服务请求和响应做统一管理。
为什么要用网关
API 网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。
什么是 gateway
Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,Spring Cloud Gateway 旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式。Spring Cloud Gateway 作为 Spring Cloud 生态系中的网关,目标是替代 ZUUL,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控 / 埋点,和限流等。
gateway 工作原理
客户端向 Spring Cloud 网关发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关 Web 处理程序。该处理程序运行通过特定于请求的过滤器链发送请求。过滤器由虚线分隔的原因是,过滤器可以在发送代理请求之前或之后执行逻辑。执行所有“前置”过滤器逻辑,然后发出代理请求。发出代理请求后,将执行“后”过滤器逻辑。
路由规则
路由和过滤器是 gateway 中非常重要的两个概念,gateway 本身提供了非常丰富的路由规则和多种过滤器来适配我们的需求。gateway 提供了 11 种路由规则,分别是:
- 后置路由谓词工厂
该谓词匹配在当前日期时间之后发生的请求。参数名为 After
- 前置路由谓词工厂
该谓词匹配当前日期时间之前发生的请求。参数名为 Before
- 时间段路由谓词工厂
该谓词匹配在 datetime1 之后和 datetime2 之前发生的请求。参数名为 Between
- cookie 路由谓词工厂
该谓词匹配具有给定名称的 cookie,并且值匹配正则表达式。参数名为 Cookie
- 标头路由谓词工厂
该谓词与具有给定名称的标头匹配,并且值与正则表达式匹配。参数名为 Header
- 主机路由谓词工厂
该谓词是指由路由进行匹配,匹配多个路由时用, 隔开。参数名为 Host
- 方法路由谓词工厂
该参数是一个或多个要匹配的 HTTP 方法。参数名为 Method
- 路径路由谓词工厂
该谓词是指在请求路径上加一个前缀,以此来匹配。参数名为 Path
- 查询路由谓词工厂
- RemoteAddr 路由谓词工厂
- 重量路线谓词工厂
其中,我们比较常用的就是 路径路由谓词工厂,配合 StripPrefix GatewayFilter 工厂,实现我们的路由匹配转发。
路径路由谓词工厂配置如下:
spring:
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
routes:
# 路由 id,建议配合服务名
- id: demo_route
#匹配路由名
uri: lb://demo-provider
predicates:
# 断言,路径相匹配的进行路由
- Path=/demo/**
配置的含义就是,如果请求路径中是 /demo/**,则转发到 demo-provider 服务。
网关过滤器
在 spring cloud gateway 2.2.2.RELEASE 版本中,已经默认实现了 30 种过滤器。
序号 | 过滤器工厂 | 作用 | 参数 |
---|---|---|---|
1 | AddRequestHeader | 为原始请求添加 Header | Header 的名称及值 |
2 | AddRequestParameter | 为原始请求添加请求参数 | 参数名称及值 |
3 | AddResponseHeader | 为原始响应添加 Header | Header 的名称及值 |
4 | DedupeResponseHeader | 剔除响应头中重复的值 | 需要去重的 Header 名称及去重策略 |
5 | Hystrix | 为路由引入 Hystrix 的断路器保护 | HystrixCommand 的名称 |
6 | CircuitBreaker | 为路由引入 Resilience4J 断路器保护 | CircuitBreaker 的名称 |
7 | FallbackHeaders | 为 fallbackUri 的请求头中添加具体的异常信息 | Header 的名称 |
MapRequestHeader | 更新原始请求中的 Header | Header 的值 | |
9 | PrefixPath | 为原始请求头添加前缀 | 前缀路径 |
10 | PreserveHostHeader | 为请求添加 preserverHostHeader=true 的属性,路由过滤器会检查该属性以决定是否要发送原始的 host | 无 |
11 | RequestRateLimiter | 用于对请求限流,限流算法为令牌桶 | keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus |
12 | RedirectTo | 将原始请求重定向到指定的 url | http 状态码及重定向的 url |
13 | RemoveHopByHopHeadersFilter | 为原始请求删除 IETF 组织规定的一系列 Header | 默认就会启用,可以通过配置指定仅删除哪些 Header |
14 | RemoveRequestHeader | 为原始请求删除某个 Header | Header 名称 |
15 | RemoveResponseHeader | 为原始响应删除某个 Header | Header 名称 |
16 | RewritePath | 重写原始的请求路径 | 原始路径正则表达式以及重写后路径的正则表达式 |
RewriteLocationResponseHeader | 重写响应头的Location 的值 |
||
18 | RewriteResponseHeader | 重写原始响应中的某个 Header | Header 名称,值的正则表达式,重写后的值 |
19 | SaveSession | 在转发请求之前,强制执行 WebSession::save 操作 |
无 |
20 | SecureHeaders | 为原始响应添加一系列起安全作用的响应头 | 无,支持修改这些安全响应头的值 |
21 | SetPath | 修改原始的请求路径 | 修改后的值 |
22 | SetRequestHeader | 修改原始请求中的某个 Header 的值 | Header 名称,修改后的值 |
23 | SetResponseHeader | 修改原始响应中某个 Header 的值 | Header 名称,修改后的值 |
24 | SetStatus | 修改原始响应的状态码 | HTTP 状态码,可以是数字,也可以是字符串 |
25 | StripPrefix | 用于截断原始请求的路径 | 使用数字表示要截断的路径的数量 |
26 | Retry | 针对不同的响应进行重试 | retries、statuses、methods、series |
27 | RequestSize | 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large
|
请求包大小,单位为字节,默认值为 5M |
28 | ModifyRequestBody | 在转发请求之前修改原始请求体内容 | 修改后的请求体内容 |
29 | ModifyResponseBody | 修改原始响应体的内容 | 修改后的响应体内容 |
30 | Default | 为所有路由添加过滤器 | 过滤器工厂名称及值 |
这里比较常用的如第 25 种,配置如下:
spring:
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
routes:
# 路由 id,建议配合服务名
- id: demo_route
#匹配路由名
uri: lb://demo-provider
predicates:
# 断言,路径相匹配的进行路由
- Path=/demo/**
filters:
- StripPrefix=1
一般情况下我们配合 path 路由使用,这里的意思是假如,我们的 demo-provider 服务种有一个 /test 的接口,实际上我们的请求路径经过网关时应该时 /demo/test,这样就能把这个路由分发到 demo-provider 服务中,但是分发过去的路由是 /demo/test,和我们实际的 /test 接口不一样。这时候我们用 StripPrefix=1,来截取掉一级路由,这样转发过去的路由就是 /test 了。
自定义网关过滤器
除了上面提供的 30 种过滤器外,我们还可以实现自定义的过滤器。
1. 实现 GatewayFilter 接口和 Ordered 接口
gatewayFilter 接口是为了实现请求过滤,ordered 接口是为了给过滤器设定优先级,值越大级别越低。
想要实现一个自定义的过滤器,无非就是两个步骤:1. 实现过滤器,2. 将过滤器添加到具体路由上。
public class TokenGatewayFilter 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;}
}
@Configuration
class RouteConfiguration{
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder){return builder.routes().route( r->
r.path("/demo/**")
.uri("lb://demo-provider")
.filter(new TokenGatewayFilter())
.id("demo_route"))
.build();}
}
2. 继承 AbstractGatewayFilterFactory 类
@Component
public class TokenCheckGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenCheckGatewayFilterFactory.Config> {public TokenCheckGatewayFilterFactory() {super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {return Arrays.asList("enabled");
}
@Override
public GatewayFilter apply(Config config) {return (exchange, chain) -> {system.out.println("这里处理自身逻辑")
return chain.filter(exchange);
};
}
public static class Config {
// 控制是否开启认证
private boolean enabled = true;
public Config() {}
public boolean isEnabled() {return enabled;}
public void setEnabled(boolean enabled) {this.enabled = enabled;}
}
}
这里我们可以直接在 application.yml 中为需要过滤的路由添加这个过滤器。
spring:
cloud:
gateway:
routes:
- id: demo_route # 路由 id,建议配合服务名
uri: lb://demo-provider #匹配路由名
predicates:
- Path=/demo/** # 断言,路径相匹配的进行路由
filters:
- TokenCheck=true
需要注意的是,这个地方自定义的过滤器名称必须是 XXGatewayFilterFactory,并且配置文件中配置过滤器时名字必须时这个 XX。
当然,我们也可以为每个路由都添加这个过滤器,可以直接这样写配置,而不用在每个路由上都去写。
spring:
cloud:
gateway:
default-filters:
- TokenCheck=true
3. 实现 GlobalFilter 和 ordered
这个 GlobalFilter 从名字中就可以看出,是一个全局过滤器,也就是说实现这个接口后,所有的请求都会被过滤,我们就不需要在去找往某个路由中加过滤器了。
@Component
public class TokenGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {System.out.println("这里处理自身逻辑");
return chain.filter(exchange);
}
@Override
public int getOrder() {return 0;}
}
以上就是实现自定义网关过滤器的三种方式了。实际开发中根据需求来实现合适的过滤器就可以了。