这篇 SpringCloud GateWay 详解,你用的到
点赞再看,养成习惯,微信搜索【牧小农】关注我获取更多资讯,风里雨里,小农等你,很高兴能够成为你的朋友。项目源码地址:公众号回复 sentinel,即可免费获取源码
背景
在微服务架构中,通常一个系统会被拆分为多个微服务,面对这么多微服务客户端应该如何去调用呢?如果没有其他更优方法,我们只能记录每个微服务对应的地址,分别去调用,但是这样会有很多的问题和潜在因素。
客户端多次请求不同的微服务,会增加客户端代码和配置的复杂性,维护成本比价高。
认证复杂,每个微服务可能存在不同的认证方式,客户端去调用,要去适配不同的认证,
存在跨域的请求,调用链有一定的相对复杂性(防火墙 / 浏览器不友好的协议)。
难以重构,随着项目的迭代,可能需要重新划分微服务
为了解决上面的问题,微服务引入了 网关 的概念,网关为微服务架构的系统提供简单、有效且统一的 API 路由管理,作为系统的统一入口,提供内部服务的路由中转,给客户端提供统一的服务,可以实现一些和业务没有耦合的公用逻辑,主要功能包含认证、鉴权、路由转发、安全策略、防刷、流量控制、监控日志等。
网关在微服务中的位置:
网关对比
Zuul 1.0 : Netflix 开源的网关,使用 Java 开发,基于 Servlet 架构构建,便于二次开发。因为基于 Servlet 内部延迟严重,并发场景不友好,一个线程只能处理一次连接请求。
Zuul 2.0 : 采用 Netty 实现异步非阻塞编程模型,一个 CPU 一个线程,能够处理所有的请求和响应,请求响应的生命周期通过事件和回调进行处理,减少线程数量,开销较小
GateWay : 是 Spring Cloud 的一个全新的 API 网关项目,替换 Zuul 开发的网关服务,基于 Spring5.0 + SpringBoot2.0 + WebFlux(基于⾼性能的 Reactor 模式响应式通信框架 Netty,异步⾮阻塞模型)等技术开发,性能高于 Zuul
Nginx+lua : 性能要比上面的强很多,使用 Nginx 的反向代码和负载均衡实现对 API 服务器的负载均衡以及高可用,lua 作为一款脚本语言,可以编写一些简单的逻辑,但是无法嵌入到微服务架构中
Kong : 基于 OpenResty(Nginx + Lua 模块)编写的高可用、易扩展的,性能高效且稳定,支持多个可用插件(限流、鉴权)等,开箱即可用,只支持 HTTP 协议,且二次开发扩展难,缺乏更易用的管理和配置方式
GateWay
官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-starter
Spring Cloud Gateway 是 Spring Cloud 的一个全新的 API 网关项目,目的是为了替换掉 Zuul1,它基于 Spring5.0 + SpringBoot2.0 + WebFlux(基于⾼性能的 Reactor 模式响应式通信框架 Netty,异步⾮阻塞模型)等技术开发,性能⾼于 Zuul,官⽅测试,Spring Cloud GateWay 是 Zuul 的 1.6 倍 ,旨在为微服务架构提供⼀种简单有效的统⼀的 API 路由管理⽅式。
可以与 Spring Cloud Discovery Client(如 Eureka)、Ribbon、Hystrix 等组件配合使用,实现路由转发、负载均衡、熔断、鉴权、路径重写、⽇志监控等
Gateway 还内置了限流过滤器,实现了限流的功能。
设计优雅,容易拓展
基本概念
路由(Route)是 GateWay 中最基本的组件之一,表示一个具体的路由信息载体,主要由下面几个部分组成:
id:路由唯一标识,区别于其他的 route
url: 路由指向的目的地 URL,客户端请求最终被转发到的微服务
order: 用于多个 Route 之间的排序,数值越小越靠前,匹配优先级越高
predicate:断言的作用是进行条件判断,只有断言为 true,才执行路由
filter: 过滤器用于修改请求和响应信息
核心流程
核心概念:
Gateway Client
向Spring Cloud Gateway
发送请求请求首先会被
HttpWebHandlerAdapter
进行提取组装成网关上下文然后网关的上下文会传递到
DispatcherHandler
,它负责将请求分发给RoutePredicateHandlerMapping
RoutePredicateHandlerMapping
负责路由查找,并根据路由断言判断路由是否可用如果过断言成功,由
FilteringWebHandler
创建过滤器链并调用通过特定于请求的
Fliter
链运行请求,Filter
被虚线分隔的原因是 Filter 可以在发送代理请求之前(pre)和之后(post)运行逻辑执行所有 pre 过滤器逻辑。然后进行代理请求。发出代理请求后,将运行“post”过滤器逻辑。
处理完毕之后将
Response
返回到Gateway
客户端
Filter 过滤器:
Filter 在 pre 类型的过滤器可以做参数效验、权限效验、流量监控、日志输出、协议转换等。
Filter 在 post 类型的过滤器可以做响应内容、响应头的修改、日志输出、流量监控等
核心思想
当用户发出请求达到 GateWay
之后,会通过一些匹配条件,定位到真正的服务节点,并且在这个转发过程前后,进行一些细粒度的控制,其中 Predicate(断言) 是我们的匹配条件,Filter 是一个拦截器,有了这两点,再加上 URL,就可以实现一个具体的路由,核心思想:路由转发+执行过滤器链
这个过程就好比考试,我们考试首先要找到对应的考场,我们需要知道考场的地址和名称(id 和 url),然后我们进入考场之前会有考官查看我们的准考证是否匹配(断言),如果匹配才会进入考场,我们进入考场之后,(路由之前)会进行身份的登记和考试的科目,填写考试信息,当我们考试完成之后(路由之后)会进行签字交卷,走出考场,这个就类似我们的过滤器
Route(路由) :构建网关的基础模块,由 ID、目标 URL、过滤器等组成
Predicate(断言) :开发人员可以匹配 HTTP 请求中的内容(请求头和请求参数),如果请求断言匹配贼进行路由
Filter(过滤) :GateWayFilter 的实例,使用过滤器,可以在请求被路由之前或者之后对请求进行修改
框架搭建
通过上述讲解已经了解了基础概念,我们来动手搭建一个GateWay
项目,来看看它到底是如何运行的新建项目:cloud-alibaba-gateway-9006
版本对应:
GateWay 属于 SprinigCloud 且有 web 依赖,在我们导入对应依赖时,要注意版本关系,我们这里使用的版本是 2.2.x 的版本,所以配合使用的Hoxton.SR5
版本
在这里我们要注意的是引入 GateWay 一定要删除 spring-boot-starter-web 依赖,否则会有冲突无法启动
父类 pom 引用:
子类 POM 引用:
yml 配置
我们在之前的cloud-alibaba-nacos-9001
项目中添加下面测试代码
启动 Nacos、cloud-alibaba-nacos-9001
、cloud-alibaba-gateway-9006
通过 gateway 网关去访问 9001 的 mxn/order 看看。
首先我们在 Nacos 中看到我们服务是注册到 Nacos 中了
然后我们访问http://localhost:9001/mxn/hello
,确保是成功的,在通过http://localhost:9006/mxn/hello
去访问,也是 OK,说明我们 GateWay 搭建成功,我们进入下一步
在上述方法中我们是通过 YML 去完成的配置,GateWay
还提供了另外一种配置方式,就是通过代码的方式进行配置,@Bean
注入一个 RouteLocator
我们可以将路由注释掉之后看一下,重启 9006 服务,访问地址http://localhost:9006/mxn/hello
就可以转发到 9001 中具体的接口中
这里并不推荐,使用代码的方式来进行配置gateWay
,大家有个了解就可以,因为代码的配置维护的成本比较高,而且对于一些需要修改的项,需要改代码才可以完成,这样不利于维护和拓展,所以还是推荐大家使用 yml 进行配置。
GateWay 负载均衡
在上述的讲解中,我们已经掌握了 GateWay
的一些基本配置和两种使用方式,下面我们就来讲解一下 GateWay
如何实现负载均衡
我们只需要在 9006 中添加lb://nacos-provider
就可以显示负载均衡。
当我们去访问http://localhost:9006/mxn/hello
的时候,就可以看到 9001 和 9002 不停的切换
Predicate 断言
在这一篇中我们来研究一下 断言 ,我们可以理解为:当满足条件后才会进行转发路由,如果是多个,那么多个条件需要同时满足
在官方提供的断言种类有 11 种(最新的有 12 种类型):
After:匹配在指定日期时间之后发生的请求。
Before:匹配在指定日期之前发生的请求。
Between:需要指定两个日期参数,设定一个时间区间,匹配此时间区间内的请求。
Cookie:需要指定两个参数,分别为 name 和 regexp(正则表达式),也可以理解 Key 和 Value,匹配具有给定名称且其值与正则表达式匹配的 Cookie。
Header:需要两个参数 header 和 regexp(正则表达式),也可以理解为 Key 和 Value,匹配请求携带信息。
Host:匹配当前请求是否来自于设置的主机。
Method:可以设置一个或多个参数,匹配 HTTP 请求,比如 GET、POST
Path:匹配指定路径下的请求,可以是多个用逗号分隔
Query:需要指定一个或者多个参数,一个必须参数和一个可选的正则表达式,匹配请求中是否包含第一个参数,如果有两个参数,则匹配请求中第一个参数的值是否符合正则表达式。
RemoteAddr:匹配指定 IP 或 IP 段,符合条件转发。
Weight:需要两个参数 group 和 weight(int),实现了路由权重功能,按照路由权重选择同一个分组中的路由
1. After : 表示配置时间之后才进行转发
时间戳获取代码,用于时间代码的获取:
如果在时间段之前访问则 404
Before
匹配ZonedDateTime
类型的时间,表示匹配在指定日期时间之前的请求,之后的请求则拒绝 404 错误
BetweenBetween
可以匹配ZonedDateTime
类型的时间,由两个ZonedDateTime
参数组成,第一个参数为开始时间,第二参数为结束时间,逗号进行分隔,匹配在指定的开始时间与结束时间之内的请求,配置如下:
Cookie
由两个参数组成,分别为name(Key)
和regexp(正则表达式)(Value
),匹配具有给定名称且其值与正则表达式匹配的 Cookie。
路由规则会通过获取 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果匹配不上则不执行。
小写字母匹配成功:
数字匹配不成功:
Header
由两个参数组成,第一个参数为Header名称
,第二参数为Header的Value值
,指定名称的其值和正则表达式相匹配的 Header 的请求
请求头携带数字断言请求成功,
断言字母匹配失败:
Host
匹配当前请求是否来自于设置的主机。
满足 Host 断言,请求成功
不满足 Host 断言失败
**Method **
可以设置一个或多个参数,匹配 HTTP 请求,比如POST,PUT,GET,DELETE
GET 断言成功
PUT 断言请求失败
Query
由两个参数组成,第一个为参数名称(必须),第二个为参数值(可选-正则表达式),匹配请求中是否包含第一个参数,如果有两个参数,则匹配请求中第一个参数的值是否符合第二个正则表达式。
断言匹配 请求成功
RemoteAddr
参数由 CIDR 表示法(IPv4 或 IPv6)字符串组成,也就是匹配的 ID 地址,配置如下:
RemoteAddr
需要两个参数 group 和 weight(int)权重数值,实现了路由权重功能,表示将相同的请求根据权重跳转到不同的 uri 地址,要求 group 的名称必须一致
直接访问http://localhost:9006/
可以看到我们请求的地址成 8/2 比例交替显示, 80% 的流量转发到https://blog.csdn.net/qq_14996421,将约 20% 的流量转发到https://juejin.cn/user/2700056290405815
Predicate 就是为了实现一组匹配规则,让请求过来找到对应的 Route 进行处理。如果有多个断言则全部命中后进行处理
GateWay Filter
路由过滤器允许修改传入的 HTTP 请求或者返回的 HTTP 响应,路由过滤器的范围是特定的路由.
Spring Cloud GateWay 内置的 Filter 生命周期有两种:pre(业务逻辑之前)、post(业务逻辑之后)
GateWay 本身自带的 Filter 分为两种: GateWayFilter(单一)、GlobalFilter(全局)
GateWay Filter 提供了丰富的过滤器的使用,单一的有 32 种,全局的有 9 种,有兴趣的小伙伴可以了解一下。
官方参考网址:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters
StripPrefix
StripPrefix 在我们当前请求中,通过规则值去掉某一部分地址,比如我们有一台服务中加入了一个前端nacos-provider
想要通过这个去访问,我们在项目cloud-alibaba-nacos-9001
中加入 context-path
现在 9001 的访问路径变为http://localhost:9001/nacos-provider/mxn/hello
,但是如果我们通过网关去访问路径就会变成http://localhost:9006/mxn/nacos-provider/mxn/hello
这个时候我们通过这个路径去访问是访问不成功的,想要解决这个方法,这个就用到了我们FIlter
中的 StripPrefix
我们重新启动 9006 项目,再去访问
自定义 Filter
虽然 Gateway 给我们提供了丰富的内置 Filter,但是实际项目中,自定义 Filter 的场景非常常见,因此单独介绍下自定义 FIlter 的使用。
想要实现 GateWay 自定义过滤器,那么我们需要实现 GatewayFilter 接口和 Ordered 接口
当我们访问http://localhost:9006/mxn/nacos-provider/mxn/hello
请求,没有携带 ID 参数,请求失败
当我们访问http://localhost:9006/mxn/nacos-provider/mxn/hello?id=1
请求,请求成功
总结
到这里我们的GateWay
就讲解完了,对于 GateWay 的核心点主要有三个Route\Predicate\Filter
,我们搞懂了这三点,基本上对于GateWay
的知识就掌握的差不多了,GateWay 核心的流程就是:路由转发+执行过滤器链,如果对文中有疑问的小伙伴,欢迎留言讨论。
创作不易,如果文中对你有帮助,记得点赞关注,您的支持是我创作的最大动力。
我是牧小农,怕什么真理无穷,进一步有进一步的欢喜~
版权声明: 本文为 InfoQ 作者【牧小农】的原创文章。
原文链接:【http://xie.infoq.cn/article/864c4288fc68488c4ce99504e】。文章转载请联系作者。
评论