高可用怎么设计呢

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 《高可用》系列

何为限流?为什么要限流?

针对软件系统来说,限流就是对请求的速率进行限制,避免瞬时的大量请求击垮软件系统。毕竟,软件系统的处理能力是有限的。如果说超过了其处理能力的范围,软件系统可能直接就挂掉了。

限流可能会导致用户的请求无法被正确处理,不过,这往往也是权衡了软件系统的稳定性之后得到的最优解。

现实生活中,处处都有限流的实际应用,就比如排队买票是为了避免大量用户涌入购票而导致售票员无法处理。

chrome_Mq9j23mc8c.png

常见限流算法

简单介绍 4 种非常好理解并且容易实现的限流算法!

图片来源于 InfoQ 的一篇文章《分布式服务限流实战,已经为你排好坑了》open in new window

#固定窗口计数器算法

固定窗口其实就是时间窗口。固定窗口计数器算法 规定了我们单位时间处理的请求数量。

假如我们规定系统中某个接口 1 分钟只能访问 33 次的话,使用固定窗口计数器算法的实现思路如下:

  • 给定一个变量 counter 来记录当前接口处理的请求数量,初始值为 0(代表接口当前 1 分钟内还未处理请求)。
  • 1 分钟之内每处理一个请求之后就将 counter+1 ,当 counter=33 之后(也就是说在这 1 分钟内接口已经被访问 33 次的话),后续的请求就会被全部拒绝。
  • 等到 1 分钟结束后,将 counter 重置 0,重新开始计数。

这种限流算法无法保证限流速率,因而无法保证突然激增的流量。

就比如说我们限制某个接口 1 分钟只能访问 1000 次,该接口的 QPS 为 500,前 55s 这个接口 1 个请求没有接收,后 1s 突然接收了 1000 个请求。然后,在当前场景下,这 1000 个请求在 1s 内是没办法被处理的,系统直接就被瞬时的大量请求给击垮了。

#滑动窗口计数器算法

滑动窗口计数器算法 算的上是固定窗口计数器算法的升级版。

滑动窗口计数器算法相比于固定窗口计数器算法的优化在于:它把时间以一定比例分片 。

例如我们的接口限流每分钟处理 60 个请求,我们可以把 1 分钟分为 60 个窗口。每隔 1 秒移动一次,每个窗口一秒只能处理 不大于 60(请求数)/60(窗口数) 的请求, 如果当前窗口的请求计数总和超过了限制的数量的话就不再处理其他请求。

很显然, 当滑动窗口的格子划分的越多,滑动窗口的滚动就越平滑,限流的统计就会越精确。

chrome_iMzSJ4EA6X.png

漏桶算法

我们可以把发请求的动作比作成注水到桶中,我们处理请求的过程可以比喻为漏桶漏水。我们往桶中以任意速率流入水,以一定速率流出水。当水超过桶流量则丢弃,因为桶容量是不变的,保证了整体的速率。

如果想要实现这个算法的话也很简单,准备一个队列用来保存请求,然后我们定期从队列中拿请求来执行就好了(和消息队列削峰/限流的思想是一样的)。

chrome_KVTYjjNuwq.png

令牌桶算法

令牌桶算法也比较简单。和漏桶算法算法一样,我们的主角还是桶(这限流算法和桶过不去啊)。不过现在桶里装的是令牌了,请求在被处理之前需要拿到一个令牌,请求处理完毕之后将这个令牌丢弃(删除)。我们根据限流大小,按照一定的速率往桶里添加令牌。如果桶装满了,就不能继续往里面继续添加令牌了。

chrome_OktaUfJHGg.png

单机限流

单机限流可以直接使用 Google Guava 自带的限流工具类 RateLimiter 。 RateLimiter 基于令牌桶算法,可以应对突发流量。

Guava 地址:https://github.com/google/guava

除了最基本的令牌桶算法(平滑突发限流)实现之外,Guava 的RateLimiter还提供了 平滑预热限流 的算法实现。

平滑突发限流就是按照指定的速率放令牌到桶里,而平滑预热限流会有一段预热时间,预热时间之内,速率会逐渐提升到配置的速率。

我们下面通过两个简单的小例子来详细了解吧!

我们直接在项目中引入 Guava 相关的依赖即可使用。

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.0.1-jre</version>
</dependency>

下面是一个简单的 Guava 平滑突发限流的 Demo。

import com.google.common.util.concurrent.RateLimiter;
/**
 * 微信搜 JavaGuide 回复"面试突击"即可免费领取个人原创的 Java 面试手册
 *
 * @author Guide哥
 * @date 2021/10/08 19:12
 **/
public class RateLimiterDemo {
    public static void main(String[] args) {
        // 1s 放 5 个令牌到桶里也就是 0.2s 放 1个令牌到桶里
        RateLimiter rateLimiter = RateLimiter.create(5);
        for (int i = 0; i < 10; i++) {
            double sleepingTime = rateLimiter.acquire(1);
            System.out.printf("get 1 tokens: %ss%n", sleepingTime);
        }
    }
}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

输出:

get 1 tokens: 0.0s
get 1 tokens: 0.188413s
get 1 tokens: 0.197811s
get 1 tokens: 0.198316s
get 1 tokens: 0.19864s
get 1 tokens: 0.199363s
get 1 tokens: 0.193997s
get 1 tokens: 0.199623s
get 1 tokens: 0.199357s
get 1 tokens: 0.195676s

1

2

3

4

5

6

7

8

9

10

下面是一个简单的 Guava 平滑预热限流的 Demo。

import com.google.common.util.concurrent.RateLimiter;
import java.util.concurrent.TimeUnit;
/**
 * 微信搜 JavaGuide 回复"面试突击"即可免费领取个人原创的 Java 面试手册
 *
 * @author Guide哥
 * @date 2021/10/08 19:12
 **/
public class RateLimiterDemo {
    public static void main(String[] args) {
        // 1s 放 5 个令牌到桶里也就是 0.2s 放 1个令牌到桶里
        // 预热时间为3s,也就说刚开始的 3s 内发牌速率会逐渐提升到 0.2s 放 1 个令牌到桶里
        RateLimiter rateLimiter = RateLimiter.create(5, 3, TimeUnit.SECONDS);
        for (int i = 0; i < 20; i++) {
            double sleepingTime = rateLimiter.acquire(1);
            System.out.printf("get 1 tokens: %sds%n", sleepingTime);
        }
    }
}

输出:

get 1 tokens: 0.0s
get 1 tokens: 0.561919s
get 1 tokens: 0.516931s
get 1 tokens: 0.463798s
get 1 tokens: 0.41286s
get 1 tokens: 0.356172s
get 1 tokens: 0.300489s
get 1 tokens: 0.252545s
get 1 tokens: 0.203996s
get 1 tokens: 0.198359s

另外,Bucket4j 是一个非常不错的基于令牌/漏桶算法的限流库。

Bucket4j 地址:https://github.com/vladimir-bukhtoyarov/bucket4j

相对于,Guava 的限流工具类来说,Bucket4j 提供的限流功能更加全面。不仅支持单机限流和分布式限流,还可以集成监控,搭配 Prometheus 和 Grafana 使用。

不过,毕竟 Guava 也只是一个功能全面的工具类库,其提供的开箱即用的限流功能在很多单机场景下还是比较实用的。

Spring Cloud Gateway 中自带的单机限流的早期版本就是基于 Bucket4j 实现的。后来,替换成了 Resilience4j。

Resilience4j 是一个轻量级的容错组件,其灵感来自于 Hystrix。自Netflix 宣布不再积极开发 Hystrixopen in new window 之后,Spring 官方和 Netflix 都更推荐使用 Resilience4j 来做限流熔断。

Resilience4j 地址: https://github.com/resilience4j/resilience4j

一般情况下,为了保证系统的高可用,项目的限流和熔断都是要一起做的。

Resilience4j 不仅提供限流,还提供了熔断、负载保护、自动重试等保障系统高可用开箱即用的功能。并且,Resilience4j 的生态也更好,很多网关都使用 Resilience4j 来做限流熔断的。

因此,在绝大部分场景下 Resilience4j 或许会是更好的选择。如果是一些比较简单的限流场景的话,Guava 或者 Bucket4j 也是不错的选择。

分布式限流

分布式限流常见的方案:

  • 借助中间件架限流 :可以借助 Sentinel 或者使用 Redis 来自己实现对应的限流逻辑。
  • 网关层限流 :比较常用的一种方案,直接在网关层把限流给安排上了。不过,通常网关层限流通常也需要借助到中间件/框架。就比如 Spring Cloud Gateway 的分布式限流实现RedisRateLimiter就是基于 Redis+Lua 来实现的,再比如 Spring Cloud Gateway 还可以整合 Sentinel 来做限流。

如果你要基于 Redis 来手动实现限流逻辑的话,建议配合 Lua 脚本来做。

网上也有很多现成的脚本供你参考,就比如 Apache 网关项目 ShenYu 的 RateLimiter 限流插件就基于 Redis + Lua 实现了令牌桶算法/并发令牌桶算法、漏桶算法、滑动窗口算法。

ShenYu 地址: https://github.com/apache/incubator-shenyu

chrome_F5Jbe6dYBj.png

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
负载均衡 Dubbo 算法
集群容错架构设计
集群容错架构设计
32 1
集群容错架构设计
|
5月前
|
存储 运维 监控
如何设计高可用的分布式系统
【7月更文挑战第29天】设计高可用的分布式系统是一个复杂而细致的过程,需要从架构设计、冗余策略、故障转移与恢复、监控与告警等多个方面综合考虑。通过采用微服务架构、无状态服务、负载均衡、数据冗余、服务冗余、跨地域部署等策略,可以显著提高系统的可用性和可靠性。同时,建立完善的监控和告警体系,确保对系统的任何变化都能及时感知和处理。最终,通过不断的优化和改进,实现系统的高可用性目标。
|
5月前
|
运维 负载均衡 监控
如何设计一个高可用的分布式系统?
如何设计一个高可用的分布式系统?
|
6月前
|
负载均衡 监控 网络协议
高可用架构设计
高可用架构设计
278 1
|
存储 消息中间件 运维
高可用架构和系统设计思想
本文从研发规范层面、应用服务层面、存储层面、产品层面、运维部署层面、异常应急层面这六大层面去剖析一个高可用的系统需要有哪些关键的设计和考虑
|
缓存 监控 容灾
0-1设计高可用、高并发、高伸缩的分布式项目架构
0-1设计高可用、高并发、高伸缩的分布式项目架构
119 0
|
设计模式 负载均衡 算法
【高可用架构】高可用性架构模式
随着企业客户部署的任务关键型基于web的服务的数量不断增加,对设计最佳网络可用性解决方案的深入理解的需求前所未有地重要。高可用性(HA)已成为此类系统开发的关键方面。高可用性简单地指的是一个组件或系统持续运行一段时间。
|
消息中间件 监控 容灾
容灾、扩展场景架构设计|学习笔记
快速学习容灾、扩展场景架构设计
容灾、扩展场景架构设计|学习笔记
【系统概念】容错、高可用和灾备
容错,高可用、灾备这三个词的使用环境极易被混淆。很多时候以为这三个词的意思是相同的。
305 0
【系统概念】容错、高可用和灾备
|
缓存 数据库 API
如何设计高可用系统之故障隔离
简单来说,当功能或性能不符合预期,就是故障。减少故障的方式有多种,包括系统优化、监控、风险扫描、链路分析、变更管控、故障注入演练、故障隔离等。故障隔离是其中一种手段,并且要求在系统设计时就需要考虑清楚。
2463 0