高并发三大利器:限流、降级、加缓存

        限流(Rate Limiting)、降级(Degradation)和缓存(Caching)是提升系统稳定性和性能的三大法宝,帮助系统在面临高并发、资源紧张或依赖服务故障时,依然能够保持一定的稳定性、可用性和性能。

        本文初步介绍了上面三大法宝的作用于使用场景,并详细讲解了 限流的三种算法(令牌桶算法、漏桶算法、滑动窗口算法)、并提供了部分Java实现与算法使用测试

1. 限流(Rate Limiting)

        限流是指对系统或服务的请求进行速率控制,以避免因过多的请求而导致系统资源耗尽、崩溃或服务不可用。限流通过控制单位时间内的请求数量或并发量,来保护系统不被过高的流量压垮。

1.1 应用场景

  • API 接口服务,防止被恶意请求攻击(如 DDoS 攻击)。
  • 电商网站的秒杀活动,限制用户请求频率,确保系统平稳运行。
  • 数据库访问,防止过多的查询请求导致数据库性能下降。

1.2 三种限流算法与实现

1.2.1 令牌桶算法(Token Bucket)

        工作原理可以简化为一个固定容量的桶,这个桶以恒定的速率向其中填充令牌(tokens)。当请求到达时,如果桶中有足够的令牌,请求就会被处理,并且会消耗相应数量的令牌;如果桶中没有足够的令牌,请求可能会被延迟或者拒绝。

        Google的Guava和Redisson的限流都采用了令牌桶算法。

Java代码实现

        调用 tryConsume方法获取令牌时,首先刷新令牌桶数量:(当前时间-上次刷新时间)* 一秒刷新数量。更新数量之后,然后获取令牌

import java.util.concurrent.atomic.AtomicLong;

public class TokenBucket {
    private final long capacity; // 桶的容量
    private final long refillRate; // 每秒填充的令牌数

    private AtomicLong tokens; // 当前桶中的令牌数
    private volatile long lastRefillTime; // 上次填充令牌的时间

    public TokenBucket(long capacity, long refillRate) {
        this.capacity = capacity;
        this.refillRate = refillRate;
        this.tokens = new AtomicLong(0); // 初始化桶为满
        this.lastRefillTime = System.currentTimeMillis();
    }

    /**
     * 尝试从桶中获取指定数量的令牌
     *
     * @param tokensNeeded 需要的令牌数
     * @return 如果成功获取到足够的令牌,则返回true;否则返回false
     */
    public synchronized boolean tryConsume(int tokensNeeded) {
        // 刷新令牌桶
        refillTokens();

        if (this.tokens.get() >= tokensNeeded) {
            // 尝试减少令牌数,这里使用CAS(Compare-And-Swap)操作来确保线程安全
            while (!this.tokens.compareAndSet(this.tokens.get(), this.tokens.get() - tokensNeeded)) {
                // 如果CAS失败,可能是因为其他线程已经改变了tokens的值,所以重新尝试
            }
            return true;
        }
        return false;
    }
}
测试代码
public static void main(String[] args) {
        // 创建一个令牌桶,容量为100,每秒填充10个令牌
        TokenBucket tokenBucket = new TokenBucket(100, 10);
        for (int i = 0; i < 10; i++) {
            // 假设每个请求需要5个令牌
            if (tokenBucket.tryConsume(10)) {
                System.out.println("请求 " + (i + 1) + " 成功,消耗了10个令牌。");
            } else {
                System.out.println("请求 " + (i + 1) + " 失败,没有足够的令牌。");
            }

            // 为了演示效果,这里暂停一段时间,模拟请求的间隔时间
            try {
                Thread.sleep(500); // 暂停100毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
}

输出结果:

请求 1 失败,没有足够的令牌。
请求 2 失败,没有足够的令牌。
请求 3 成功,消耗了10个令牌。
请求 4 失败,没有足够的令牌。
请求 5 成功,消耗了10个令牌。
请求 6 失败,没有足够的令牌。
请求 7 成功,消耗了10个令牌。
请求 8 失败,没有足够的令牌。
请求 9 成功,消耗了10个令牌。
请求 10 失败,没有足够的令牌

1.2.2 漏桶算法(Leaky Bucket)

        漏桶算法(Leaky Bucket Algorithm)是一种常用的流量控制算法,主要用于控制数据的注入速率,平滑突发流量,并限制数据的最大突发量。漏桶算法可以想象为一个固定容量的桶,桶底有一个小孔,水(代表数据)以恒定的速率从桶底流出。当水(数据)注入桶中时,如果桶未满,则水(数据)会留在桶中;如果桶已满,则多余的水(数据)会被丢弃。


1.2.3 计数器(Fixed Window,固定窗口算法)

  • 原理:通过维护一个计数器变量来限制在特定时间间隔内的请求数量。在指定周期内累加访问次数,当访问次数达到设定的阈值时,触发限流策略。时间周期结束时,计数器清零并重新开始计数。
  • 优点:实现简单,直观易懂,设置明确的阈值。
  • 缺点:存在窗口切换时的“突增问题”,即在一个时间窗口的末尾和下一个时间窗口的开始,可能会因为请求量突然增加而超出限制。

1.2.4 滑动窗口算法

  • 原理:将固定窗口细分成多个小时间窗口(或称为滑动窗口),每个小窗口都有自己的计数器。随着时间的推移,窗口会向前滑动,并丢弃过期的小窗口数据。通过统计滑动窗口内的总请求数来实现限流。
  • 优点:解决了固定窗口算法的“突增问题”,提供了更平滑的流量控制。
  • 应用:Spring Cloud中的熔断框架Hystrix,以及Spring Cloud Alibaba中的Sentinel都采用滑动窗口来做数据统计。

2. 降级(Degradation)

        降级是在系统资源紧张或依赖服务故障时,为了保证系统的核心功能依然可用,对非核心功能或服务进行暂时性的降级处理。

应用场景

  • 第三方服务调用失败时,可以降级为使用本地缓存数据或默认值。
  • 系统资源不足时,关闭非关键功能,保证核心功能的正常运行。
  • 在高并发场景下,对部分请求进行限流或拒绝服务,以保护系统整体可用性。

实现方式

  • 提前设计好降级策略,如设置服务依赖的优先级,当资源不足时按优先级进行降级。
  • 使用开关控制功能是否启用,便于在紧急情况下快速切换。
  • 监控服务状态,自动触发降级逻辑。

3. 加缓存(Caching)

加缓存是通过将计算结果或数据存储在内存中或更快的存储介质上,以减少对慢速资源(如数据库)的访问次数,从而提高系统的响应速度和处理能力。

应用场景

  • 读取操作远多于写入操作的场景,如商品详情页展示。
  • 实时性要求不高的数据查询,如用户个人信息。
  • 计算成本较高的操作结果,如复杂的查询语句结果。

实现方式

  • 本地缓存:如 Java 中的 EhCache、Guava Cache。
  • 分布式缓存:如 Redis、Memcached,适用于分布式系统。
  • CDN(内容分发网络):适用于静态资源缓存,减少服务器负载。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值