一、API Gateway
API 网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:
客户端会多次请求不同的微服务,增加了客户端的复杂性。
存在跨域请求,在一定场景下处理相对复杂。
认证复杂,每个服务都需要独立认证。
难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将会很难实施。
某些微服务可能使用了防火墙 / 浏览器不友好的协议,直接访问会有一定的困难。
以上这些问题可以借助 API 网关解决。API 网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过 API 网关这一层。也就是说,API 的实现方面更多的考虑业务逻辑,而安全、性能、监控可以交由 API 网关来做,这样既提高业务灵活性又不缺安全性,典型的架构图如图所示:
二、Spring Cloud Gateway
Spring Cloud Gateway官网文档
2.1 Spring Cloud Gateway简介
Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
Spring Cloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Netflix Zuul,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
由于Spring 5.0支持 Netty,Http2,而Spring Boot 2.0支持Spring 5.0,因此Spring Cloud Gateway支持 Netty和Http2。
补充:
1、Zuul(1.x) 基于 Servlet,使用阻塞 API,它不支持任何长连接 ,如 WebSockets。
2、Zuul(2.x) 基于Netty。
3、Spring Cloud GateWay天⽣就是异步⾮阻塞的,基于Reactor模型,支持 WebSockets,支持限流等新特性。
4、Spring Cloud 已经不再集成 Zuul 2.x 。
2.2 Spring Cloud Gateway相关概念
Route(路由):这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。
Predicate(断言):这是一个 Java 8 的 Predicate。输入类型是一个 ServerWebExchange。我们可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。
Filter(过滤器):这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,我们可以使用它修改请求和响应。
2.3 Spring Cloud Gateway工作流程
客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。 过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
三、项目[ac-mall-cloud]接入Spring Cloud Gateway
3.1 新建module[gateway-service]
1、选中ac-mall-cloud项目,选择新建module
2、选择maven工程
3、填写module名称
4、完成效果
3.2 maven配置
在module [gateway-service]pom.xml中引入Spring Cloud Gateway依赖和Nacos依赖,Spring Cloud Gateway 是使用 netty+webflux 实现因此不需要再引入 web 模块。
org.springframework.cloud
spring-cloud-starter-gateway
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
module [gateway-service]pom.xml全配置如下
org.springframework.boot
spring-boot-starter-parent
2.1.9.RELEASE
4.0.0
org.ac
gateway-service
Greenwich.SR3
2.1.0.RELEASE
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
com.alibaba.cloud
spring-cloud-alibaba-dependencies
${alibaba.cloud.version}
pom
import
org.springframework.cloud
spring-cloud-starter-gateway
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
3.3 创建启动类GatewayApplication
新建com.ac.gateway
包,并创建SpringBoot启动类GatewayApplication
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class);
}
}
3.4 新建application.yml配置文件
1、配置启动端口7010
2、配置Nacos服务注册中心
server:
port: 7010
spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: 47.105.146.74:8848
项目结构如下
3.5 Spring Cloud Gateway网关路由配置方式
Spring Cloud Gateway 网关路由有两种配置方式:
- 在配置文件 yml中配置
- 通过@Bean自定义 RouteLocator,在启动主类 Application 中配置
这两种方式是等价的,建议使用 yml 方式进配置。
3.6 配置文件yml方式配置网关路由测试
server:
port: 7010
spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: 47.105.146.74:8848
gateway:
routes:
- id: jianshu_route
uri: https://www.jianshu.com
predicates:
- Query=url,jianshu
启动module[gateway-service],在浏览器中输入访问地址http://127.0.0.1:7010/?url=jianshu
,就会显示首页
3.7 配置[user-service]、[order-service]网关路由
server:
port: 7010
spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: 47.105.146.74:8848
gateway:
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: user-service-api # 当前路由的标识, 要求唯一
uri: lb://user-service # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates: # 断言(就是路由转发要满足的条件)
- Path=/users/** # 当请求路径满足Path指定的规则时,才进行路由转发
#filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
#- StripPrefix=1 # 转发之前去掉1层路径
- id: order-service-api
uri: lb://order-service
predicates:
- Path=/orders/**
依次启动module[user-service]、[order-service]、[gateway-service],打开Nacos管理平台,效果如下
1、直接访问[user-service]服务接口
http://127.0.0.1:8010/users/1
2、直接访问[order-service]服务接口
http://127.0.0.1:8020/orders/1/1
3、通过gateway访问[user-service]服务接口
http://127.0.0.1:7010/users/1
4、通过gateway访问[order-service]服务接口
http://127.0.0.1:7010/orders/1/1
四、路由规则
Spring Cloud Gateway 的功能很强大,我们仅仅通过 Predicates 的设计就可以看出来,前面我们只是使用了 predicates 进行了简单的条件匹配,其实 Spring Cloud Gataway 帮我们内置了很多 Predicates 功能。
Spring Cloud Gateway 是通过 Spring WebFlux 的 HandlerMapping 做为底层支持来匹配到转发路由,Spring Cloud Gateway 内置了很多 Predicates 工厂,这些 Predicates 工厂通过不同的 HTTP 请求参数来匹配,多个 Predicates 工厂可以组合使用。
4.1 Predicate 介绍
Predicate 来源于 Java 8,是 Java 8 中引入的一个函数,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。
在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。
说白了 Predicate 就是为了实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理,接下来我们接下 Spring Cloud GateWay 内置几种 Predicate 的使用。
4.2 通过时间匹配
Predicate 支持设置一个时间,在请求进行转发的时候,可以通过判断在这个时间之前或者之后进行转发。比如我们现在设置只有在2021年4月3日才会转发到我的网站,在这之前不进行转发,我就可以这样配置:
spring:
gateway:
routes:
- id: jianshu_route
uri: https://www.jianshu.com/
predicates:
- Query=url,jianshu
- After=2021-04-03T00:00:00+08:00[Asia/Shanghai]
备注:当前时间为2021年4月2日
Spring 是通过 ZonedDateTime 来对时间进行的对比,ZonedDateTime 是 Java 8 中日期时间功能,用于表示带时区的日期与时间信息的类,ZonedDateTime 支持通过时区来设置时间,中国的时区是:Asia/Shanghai。
After Route Predicate 是指在这个时间之后的请求都转发到目标地址。上面的示例是指,请求时间在 2021年4月3日0点0分0秒之后的所有请求都转发到目标地址。+08:00是指时间和UTC时间相差八个小时,时间地区为Asia/Shanghai。
访问http://127.0.0.1:7010/?url=jianshu
页面会报 404 没有找到地址
将配置时间改成2021年4月1日
spring:
gateway:
routes:
- id: qq_route
uri: https://www.jianshu.com/
predicates:
- Query=url,jianshu
- After=2021-04-01T00:00:00+08:00[Asia/Shanghai]
再访问http://127.0.0.1:7010/?url=jianshu
,页面就正常显示内容了
除了在时间之前或者之后外,Gateway 还支持限制路由请求在某一个时间段范围内,可以使用 Between Route Predicate 来实现。
spring:
gateway:
routes:
- id: jianshu_route
uri: https://www.jianshu.com/
predicates:
- Query=url,jianshu
- Between=2021-04-01T00:00:00+08:00[Asia/Shanghai], 2021-04-05T00:00:00+08:00[Asia/Shanghai]
这样设置就意味着在这个时间段内可以匹配到此路由,超过这个时间段范围则不会进行匹配。通过时间匹配路由的功能很酷,可以用在限时抢购的一些场景中。
4.3 通过Cookie匹配
Cookie Route Predicate可以接收两个参数,一个是Cookie name ,一个是正则表达式,路由规则会通过获取对应的Cookie name值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。
spring:
gateway:
routes:
- id: jianshu_route
uri: https://www.jianshu.com/
predicates:
- Query=url,jianshu
- Cookie=username, alanchen
使用Postman测试,在Header里设置Cookie,如果不设置Cookie,访问会报404
4.4 通过Header属性匹配
Header Route Predicate 和 Cookie Route Predicate 一样,也是接收 2 个参数,一个 header 中属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。
spring:
gateway:
routes:
- id: qq_route
uri: https://www.jianshu.com/
predicates:
- Query=url,jianshu
- Header=username,alanchen
4.5 其他匹配规则
更多匹配规则,可参考官网文档Route Predicate Factories
五、附录
项目源码地址