文章目录
1、什么是 API 网关?
不同的微服务一般会有不同的网络地址,客户端在访问微服务时,必须要记住所有微服务的地址,这样对客户端来说不太友好,且不好维护。
客户端直接与微服务之间进行通讯,会产生如下问题:
- 客户端会请求多个不同的服务,需要维护不同的请求地址
- 加大身份认证的难度,每个微服务需要独立认证
- 在某些场景下存在跨域请求的问题
为了解决这些问题,就产生了网关。网关就是介于客户端和微服务之间的中间部分,所有的外部请求都会先经过微服务网关。这样客户端只需要与网关交互,只知道一个网关地址即可。
API 网关是一个服务器,是一个系统对外的唯一入口。API 网关封装了系统内部架构,为每个客户端提供一个定制的 API。API 网关的核心要点是:所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供 REST/HTTP 的访问API,服务端通过 API-GW 注册和管理服务。
2、什么是 Gateway ?
Spring Cloud Gateway 是基于 Spring 5.0+Spring Boot 2.0 和 Project Reactor 等技术开发的网关,为微服务架构提供一种简单有效的统一的 API 路由管理方式。Spring Cloud Gateway 用于替代 Zuul ,因为在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上版本进行集成,仍然还是使用的Zuul 1.x非Reactor模式的老版本。
SpringCloud Gateway是基于 WebFlux 框架实现的,而 WebFlux 框架底层则使用了高性能的 Reactor 模式通信框架 Netty。Spring Cloud Gateway的目标提供统一的路由方式且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
3、重要概念
Route(路由)是网关最基础的部分,路由信息由一个ID、一个目的URL、一组 Predicate(断言)工厂和一组 Filter(过滤) 组成。如果断言为真,则说明请求URL和配置的路由匹配。
断言(predicates)是Java8中的断言函数,允许开发者去定义匹配来自Http Request中的任何信息,比如请求头和参数等。
过滤器(filter) 一个标准的Spring webFilter,Spring Cloud Gateway中的Filter分为两种类型,分别是全局过滤器和局部过滤器。过滤器可以在请求被路由前或者之后对请求进行修改。
4、路由的快速使用
- 创建一个新的项目:网关是一个独立的项目,没有任何的业务代码,只需要在配置文件中对路由进行配置
- 导入相关依赖:导入 Gateway 网关的依赖
<dependency> <groupId>org.springframework.cloud<groupId> <artifactId>spring-cloud-starter-gateway<artifactId> <dependency>
- 编写启动类:Gateway 网关不需要在启动类上添加注解
- 编写配置文件:在配置文件中编写对路由的配置
server: port: 9527 spring: application: name: cloud-gateway cloud: #对网关进行配置 gateway: #对网关的路由进行配置 routes: - id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates: - Path=/payment/ok/** # 断言,路径相匹配的进行路由 - id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates: - Path=/payment/lb/** # 断言,路径相匹配的进行路由
上述过程完成了一个网关的配置,原先通过http://localhost:8001/payment/ok/2
才能正确的访问服务,当配置了网关之后,可以通过http://localhost:9527/payment/ok/2
地址访问服务。
网关需要通过spring.cloud.gateway.routes
对路由进行设置,在配置时一共有下面有四个属性:
id
:我们自定义的路由 ID,保持唯一uri
:目标服务地址predicates
:断言。路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默
认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。filters
:过滤规则
5、路由规则
Gataway 内置了很多 predicates 用于进行实现各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。具体的路由匹配规则:
根据请求的时间匹配:只有在当前时间在设置的时间之后、之前或之间时,才能匹配成功。
spring: application: name: cloud-gateway cloud: gateway: routes: - id: payment_routh #payment_route uri: http://localhost:8001 predicates: - After=2021-10-20T20:43:38.974+08:00[Asia/Shanghai]#当前时间在某时间之后时可以访问 - Before=2021-10-20T20:43:38.974+08:00[Asia/Shanghai]#当前时间在某时间之前时可以访问 - Between=2021-10-20T20:43:38.974+08:00[Asia/Shanghai],2022-10-20T20:43:38.974+08:00[Asia/Shanghai]#当前时间在某两个时间段之间时可以访问
根据请求的Header,Cookieh和 Host (主机名)匹配:当前请求中必须含有某些属性时,才能匹配成功
#省略 predicates: - Cookie=username,zhangsan#匹配给定名称和正则表达式, Cookie 要有一个 username 属性且属性值为zhangsan - Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性并且值为整数的正则表达式 - Host=**.somehost.org,**.anotherhost.org #请求的主机名列表,**代表可变参数
根据请求的方式和路径匹配
#省略 predicates: - Method=GET - Path=/payment/ok/**
根据请求的参数
#省略 predicates: - Query=username, \d+
6、动态路由
在讲解快速使用是,配置的路由的 uri 都是固定的,但是在实际情况中,这些 url 不能写死,所以我们可以与注册中心配合使用,使网关自动的从注册中心中获取服务列表并访问。如下是具体的步骤:
- 导入依赖:导入注册中心所需要的依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
- 修改启动类:添加
@EnableEurekaClient
注解 - 修改配置文件:
spring: application: name: cloud-gateway cloud: gateway: discovery: locator: enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由 routes: - id: payment_routh uri: lb://CLOUD-PROVIDER-PAYMENT predicates: - Path=/payment/ok/** eureka: instance: hostname: cloud-gateway-service client: #服务提供者provider注册进eureka服务列表内 register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:7001/eureka
上述配置中,最主要的是对 url 的修改。在与注册中心配合使用时,url 必须以 lb 开头,后面接服务注册在注册中心的名称。 lb 代表从注册中心获取服务。
7、过滤器
请求可以通过 predicates 找到对应的路由进行处理,还可以在路由上设置过滤器,过滤掉一部分不符合条件的请求。在 Gateway 网关中过滤器的生命周期只有两个:
PRE
: 在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。POST
:在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端
过滤器从作用范围可分为另外两种 GatewayFilter 与 GlobalFilter:
- GatewayFilter :局部过滤器,应用到单个路由或者一个分组的路由上。必须通过spring.cloud.routes.filters 配置在具体路由。
- GlobalFilter:全局过滤器,应用到所有的路由上,不需要在配置文件中配置。
7.1、局部过滤器
局部过滤器是针对单个路由的过滤器。可以对访问的URL过滤,进行切面处理。在 Gateway 中内置了很多不同类型的局部过滤器。
过滤器 | 作用 | 参数 |
---|---|---|
AddRequestHeader | 为原始请求添加Header | Header的名称及值 |
AddRequestParameter | 为原始请求添加请求参数 | 参数名称及值 |
AddResponseHeader | 为原始响应添加Heade | r Header的名称及值 |
DedupeResponseHeader | 剔除响应头中重复的值 | 需要去重的Header名 称及去重策略 |
Hystrix | 为路由引入Hystrix的断路器保护 | HystrixCommand的名称 |
FallbackHeaders | 为fallbackUri的请求头中添加具 | 体的异常信息 Header的名称 |
PrefixPath | 为原始请求路径添加前缀 | 前缀路径 |
PreserveHostHeader | 为请求添加一个 | preserveHostHeader=true的属 性,路由过滤器会检查该属性以决定是否要发送原始的Host |
RequestRateLimiter | 用于对请求限流,限流算法为令 牌桶 | keyResolver、 rateLimiter、 statusCode、 denyEmptyKey、 emptyKeyStatus |
RedirectTo | 将原始请求重定向到指定的URL | http状态码及重定向的 url |
RemoveHopByHopHeadersFilter | 为原始请求删除IETF组织规定的 一系列Header | 默认就会启用,可以通 过配置指定仅删除哪些 Header |
RemoveRequestHeader | 为原始请求删除某个Header | Header名称 |
RemoveResponseHeader | 为原始响应删除某个Header | Header名称 |
RewritePath | 重写原始的请求路径 | 原始路径正则表达式以 及重写后路径的正则表达式 |
RewriteResponseHeader | 重写原始响应中的某个Header | Header名称,值的正则表达式,重写后的值 |
SaveSession | 在转发请求之前,强制执行 WebSession::save操作 | 无 |
secureHeaders | 为原始响应添加一系列起安全作用的响应头 | 支持修改这些安全 响应头的值 |
SetPath | 修改原始的请求路径 | 修改后的路径 |
SetResponseHeader | 修改原始响应中某个Header的值 | Header名称,修改后的值 |
SetStatus | 修改原始响应的状态码 | HTTP 状态码,可以是 数字,也可以是字符串 |
StripPrefix | 用于截断原始请求的路径 | 使用数字表示要截断的 路径的数量 |
Retry | 针对不同的响应进行重试 | retries、statuses、 methods、series |
RequestSize | 设置允许接收最大请求包的大小。如果请求包大小超过设置的 值,则返回 413 Payload Too Large | 请求包大小,单位为字 节,默认值为5M |
ModifyRequestBody | 在转发请求之前修改原始请求体内容 | 修改后的请求体内容 |
ModifyResponseBody | 修改原始响应体的内容 | 修改后的响应体内容 |
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-red, blue
- AddRequestHeader=X-Request-Red, Blue-{segment}
- AddRequestParameter=red, blue
#省略其他
Gateway 提供了31种局部过滤器,上面列举了大部分局部过滤器。每个过滤器工厂都对应一个实现类,并且这些类的名称必须以 GatewayFilterFactory 结尾,例如 AddRequestHeader 对应的实现类为AddRequestHeaderGatewayFilterFactory 。
7.2、全局过滤器
全局过滤器作用于所有路由, Gateway 也内置了很多不同类型的全局过滤器。全局过滤器只做了解。
7.3、自定义全局过滤器
内置的过滤器已经可以完成大部分的功能,但有一些业务的处理,还是需要我们自己编写自定义过滤器来实现的,想要创建一自定义过滤器,必须要继承两个接口GlobalFilter,Ordered
。具体的定义过程如下:
@Component
public class MyGateWayFilter implements GlobalFilter,Ordered
{
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("time:"+new Date()+" 执行了自定义的全局过滤器: "+"MyGateWayFilter");
//获取 request
exchange.getRequest()
if () {
System.out.println("****用户名为null,无法登录");
//为请求的响应添加状态码
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
//返回请求
return exchange.getResponse().setComplete();
}
//过滤器放行
return chain.filter(exchange);
}
@Override
public int getOrder(){
return 0;
}
}
filter
方法用于完成过滤器的逻辑处理,getOrder
方法指定此过滤器的优先级,返回值越大级别越低。
其中filter
方法的参数 ServerWebExchange 就相当于当前请求和响应的上下文,可以用于获取请求或响应信息,chain.filter
表示过滤器放行,可以去执行下一个过滤器。
8、执行流程
Gateway 的工作流程:
客户端向 Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。这部分是通过 predicates 实现的。
Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑然后返回。过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。在请求之前的过滤器类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等;在“请求之后的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。