Spring Cloud 系列之 Gateway 服务网关(四)
本篇文章为系列文章,未读第一集的同学请猛戳这里:
本篇文章讲解 Gateway 网关如何实现限流、整合 Sentinel 实现限流以及高可用网关环境搭建。
1|0网关限流
顾名思义,限流就是限制流量,就像你宽带包有 1 个 G 的流量,用完了就没了。通过限流,我们可以很好地控制系统的 QPS,从而达到保护系统的目的。
1|1为什么需要限流
比如 Web 服务、对外 API,这种类型的服务有以下几种可能导致机器被拖垮:
- 用户增长过快(好事)
- 因为某个热点事件(微博热搜)
- 竞争对象爬虫
- 恶意的请求
这些情况都是无法预知的,不知道什么时候会有 10 倍甚至 20 倍的流量打进来,如果真碰上这种情况,扩容是根本来不及的。
从上图可以看出,对内而言:上游的 A、B 服务直接依赖了下游的基础服务 C,对于 A,B 服务都依赖的基础服务 C 这种场景,服务 A 和 B 其实处于某种竞争关系,如果服务 A 的并发阈值设置过大,当流量高峰期来临,有可能直接拖垮基础服务 C 并影响服务 B,即雪崩效应。
1|2限流算法
点击链接观看:限流算法视频(获取更多请关注公众号「哈喽沃德先生」)
常见的限流算法有:
- 计数器算法
- 漏桶(Leaky Bucket)算法
- 令牌桶(Token Bucket)算法
计数器算法
计数器算法是限流算法里最简单也是最容易实现的一种算法。比如我们规定,对于 A 接口来说,我们 1 分钟的访问次数不能超过 100 个。那么我们可以这么做:在一开始的时候,我们可以设置一个计数器 counter,每当一个请求过来的时候,counter 就加 1,如果 counter 的值大于 100 并且该请求与第一个请求的间隔时间还在 1 分钟之内,触发限流;如果该请求与第一个请求的间隔时间大于 1 分钟,重置 counter 重新计数,具体算法的示意图如下:
这个算法虽然简单,但是有一个十分致命的问题,那就是临界问题,我们看下图:
从上图中我们可以看到,假设有一个恶意用户,他在 0:59 时,瞬间发送了 100 个请求,并且 1:00 又瞬间发送了 100 个请求,那么其实这个用户在 1 秒里面,瞬间发送了 200 个请求。我们刚才规定的是 1 分钟最多 100 个请求,也就是每秒钟最多 1.7 个请求,用户通过在时间窗口的重置节点处突发请求, 可以瞬间超过我们的速率限制。用户有可能通过算法的这个漏洞,瞬间压垮我们的应用。
还有资料浪费的问题存在,我们的预期想法是希望 100 个请求可以均匀分散在这一分钟内,假设 30s 以内我们就请求上限了,那么剩余的半分钟服务器就会处于闲置状态,比如下图:
漏桶算法
漏桶算法其实也很简单,可以粗略的认为就是注水漏水的过程,往桶中以任意速率流入水,以一定速率流出水,当水超过桶流量则丢弃,因为桶容量是不变的,保证了整体的速率。
漏桶算法是使用队列机制实现的。
漏桶算法主要用途在于保护它人(服务),假设入水量很大,而出水量较慢,则会造成网关的资源堆积可能导致网关瘫痪。而目标服务可能是可以处理大量请求的,但是漏桶算法出水量缓慢反而造成服务那边的资源浪费。
漏桶算法无法应对突发调用。不管上面流量多大,下面流出的速度始终保持不变。因为处理的速度是固定的,请求进来的速度是未知的,可能突然进来很多请求,没来得及处理的请求就先放在桶里,既然是个桶,肯定是有容量上限,如果桶满了,那么新进来的请求就会丢弃。
令牌桶算法
令牌桶算法是对漏桶算法的一种改进,漏桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌。
场景大概是这样的:桶中一直有大量的可用令牌,这时进来的请求可以直接拿到令牌执行,比如设置 QPS 为 100/s,那么限流器初始化完成一秒后,桶中就已经有 100 个令牌了,等服务启动完成对外提供服务时,该限流器可以抵挡瞬时的 100 个请求。当桶中没有令牌时,请求会进行等待,最后相当于以一定的速率执行。
Spring Cloud Gateway 内部使用的就是该算法,大概描述如下:
- 所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
- 根据限流大小,设置按照一定的速率往桶里添加令牌;
- 桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
- 请求到达后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;
- 令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流。
漏桶算法主要用途在于保护它人,而令牌桶算法主要目的在于保护自己,将请求压力交由目标服务处理。假设突然进来很多请求,只要拿到令牌这些请求会瞬时被处理调用目标服务。
1|3Gateway 限流
点击链接观看:Gateway 服务限流视频(获取更多请关注公众号「哈喽沃德先生」)
Spring Cloud Gateway 官方提供了 RequestRateLimiterGatewayFilterFactory
过滤器工厂,使用 Redis
和 Lua
脚本实现了令牌桶的方式。
官网文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#the-redis-ratelimiter 具体实现逻辑在 RequestRateLimiterGatewayFilterFactory
类中,Lua
脚本在如下图所示的源码文件夹中:
添加依赖
限流规则
URI 限流
配置限流过滤器和限流过滤器引用的 bean 对象。
编写限流规则配置类。
多次访问:http://localhost:9000/product/1 结果如下:
Redis 结果如下:
参数限流
配置限流过滤器和限流过滤器引用的 bean 对象。
编写限流规则配置类。
多次访问:http://localhost:9000/product/1?userId=123 结果如下:
Redis 结果如下:
IP 限流
配置限流过滤器和限流过滤器引用的 bean 对象。
编写限流规则配置类。
多次访问:http://localhost:9000/product/1 结果如下:
Redis 结果如下:
1|4Sentinel 限流
点击链接观看:Sentinel 服务限流视频(获取更多请关注公众号「哈喽沃德先生」)
Sentinel 支持对 Spring Cloud Gateway、Netflix Zuul 等主流的 API Gateway 进行限流。
官网文档:
- https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel
- https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81#spring-cloud-gateway
创建项目
创建 gateway-server-sentinel
项目。
添加依赖
单独使用添加 sentinel gateway adapter
依赖即可。
若想跟 Sentinel Starter 配合使用,需要加上 spring-cloud-alibaba-sentinel-gateway
依赖来让 spring-cloud-alibaba-sentinel-gateway
模块里的 Spring Cloud Gateway 自动化配置类生效。
同时请将 spring.cloud.sentinel.filter.enabled
配置项置为 false(若在网关流控控制台上看到了 URL 资源,就是此配置项没有置为 false)。
配置文件
限流规则配置类
使用时只需注入对应的 SentinelGatewayFilter
实例以及 SentinelGatewayBlockExceptionHandler
实例即可。
GatewayConfiguration.java
启动类
GatewayServerSentinelApplication.java
访问
多次访问:http://localhost:9001/order/1 结果如下:
接口 BlockRequestHandler
的默认实现为 DefaultBlockRequestHandler
,当触发限流时会返回默认的错误信息:Blocked by Sentinel: FlowException
。我们可以通过 GatewayCallbackManager
定制异常提示信息。
自定义异常提示
GatewayCallbackManager
的 setBlockHandler
注册函数用于实现自定义的逻辑,处理被限流的请求。
访问
多次访问:http://localhost:9001/order/1 结果如下:
分组限流
访问
访问:http://localhost:9001/product-service/product/1 触发限流
访问:http://localhost:9001/order-service/order/index 触发限流
访问:http://localhost:9001/order-service/order/1 不会触发限流
2|0高可用网关
业内通常用多少 9 来衡量网站的可用性,例如 QQ 的可用性是 4 个 9,就是说 QQ 能够保证在一年里,服务在 99.99% 的时间是可用的,只有 0.01% 的时间不可用,大约最多 53 分钟。
对于大多数网站,2 个 9 是基本可用;3 个 9 是叫高可用;4 个 9 是拥有自动恢复能力的高可用。
实现高可用的主要手段是数据的冗余备份和服务的失效转移,这两种手段具体可以怎么做呢,在网关里如何体现?主要有以下几个方向:
- 集群部署
- 负载均衡
- 健康检查
- 节点自动重启
- 熔断
- 服务降级
- 接口重试
2|1Nginx + 网关集群实现高可用网关
下载
官网:http://nginx.org/en/download.html 下载稳定版。为了方便学习,请下载 Windows 版本。
安装
解压文件后直接运行根路径下的 nginx.exe
文件即可。
Nginx 默认端口为 80,访问:http://localhost:80/ 看到下图说明安装成功。
配置网关集群
进入 Nginx 的 conf
目录,打开 nginx.conf
文件,配置网关集群:
访问
启动两台网关服务器 http://localhost:9000/
,http://localhost:9001/
和相关服务。
访问:http://localhost/product-service/product/1 实现高可用网关。
总结
一个请求过来,首先经过 Nginx 的一层负载,到达网关,然后由网关负载到真实后端,若后端有问题,网关会进行重试访问,多次访问后仍返回失败,可以通过熔断或服务降级立即返回结果。而且,由于是负载均衡,网关重试时不一定会访问到出错的后端。
至此 Gateway 服务网关所有的知识点就讲解结束了。