北冰洋以北 发表于 2024-12-10 13:46:56

如何用Redis实现限流?

https://i-blog.csdnimg.cn/direct/9998c4518fbd4798a2aa2d8ed90bf95b.gif
如何用Redis实现限流?

目次


[*]什么是限流?
[*]限流的意义
[*]限流常见算法
[*]使用Redis实现限流

[*]计数器算法
[*]滑动窗口算法
[*]漏桶算法
[*]令牌桶算法

[*]Redis限流的实践案例
[*]总结
什么是限流?

限流是指在各种应用场景中,通过技术和计谋手段对数据流量、请求频率或资源斲丧进行有筹划的限制,以避免体系负载过高、性能降落甚至崩溃的情况发生。限流的目的在于维护体系的稳定性和可用性,并确保服务质量。
限流的意义

使用限流有以下几个利益:

[*]保护体系稳定性:过多的并发请求可能导致服务器内存耗尽、CPU 使用率饱和,从而引发体系响应慢、无法正常服务的问题。
[*]防止资源滥用:确保有限的服务资源被合理公平地分配给全部用户,防止个别用户或恶意程序太过斲丧资源。
[*]优化用户体验:对于网站和应用程序而言,如果任由高并发导致响应速率变慢,会影响全部用户的正常使用体验。
[*]保障安全:在网络层面,限流有助于防范 DoS/DDoS 攻击,低沉体系遭受恶意攻击的风险。
[*]运维成本控制:合理的限流措施可以帮助企业减少不必要的硬件投入,节省运营成本。
限流常见算法

限流的常见实现算法有以下几个:

[*] 计数器算法

[*]将时间周期分别为固定巨细的窗口(如每分钟、每小时),并在每个窗口内统计请求的数量。当窗口内的请求数达到预设的阈值时,后续请求将被限制。时间窗口竣事后,计数器清零。
[*]优点:实现简单,易于明白。
[*]缺点:在窗口切换时候可能会有突刺流量问题,即在窗口竣事时会有短暂的大量请求被允许通过。

[*] 滑动窗口算法

[*]改进了计数器算法的突刺问题,将时间窗口分别为多个小的时间段(桶),每个小时间段有自己的计数器。随着时间流逝,窗口像滑块一样平移,过期的小时间段的计数会被丢弃,新时间段参加计数。全部小时间段的计数之和不能超过设定的阈值。
[*]优点:更平滑地处理流量,避免了突刺问题。
[*]缺点:实现相对复杂,需要维护多个计数器。

[*] 漏桶算法

[*]想象一个固定容量的桶,水(请求)以恒定速率流入桶中,同时桶底部有小孔让水以恒定速率流出。当桶满时,新来的水(请求)会被丢弃。此算法主要用来平滑网络流量,防止瞬时流量过大。
[*]优点:可以平滑突发流量,包管下游体系的稳定。
[*]缺点:无法处理突发流量高峰,多余的请求会被直接丢弃。

[*] 令牌桶算法

[*]与漏桶相反,有一个固定速率填充令牌的桶,令牌代表请求许可。当请求到达时,需要从桶中取出一个令牌,如果桶中有令牌则允许请求通过,否则拒绝。桶的容量是有限的,多余的令牌会被丢弃。
[*]优点:既能平滑流量,又能处理一定程度的突发流量(因为令牌可以累积)。
[*]缺点:需要精确控制令牌生成速率,实现较漏桶复杂。

使用Redis实现限流

计数器算法

此方法的实现思路是:使用一个计数器存储当前请求量(每次使用incr方法相加),并设置一个过期时间,计数器在一定时间内自动清零。
具体实现代码如下:
import redis.clients.jedis.Jedis;

public class RedisRateLimiter {
    private static final String REDIS_KEY = "request_counter";
    private static final int REQUEST_LIMIT = 100; // 限流阈值
    private static final int EXPIRE_TIME = 60; // 过期时间(秒)

    public boolean allowRequest() {
      Jedis jedis = new Jedis("localhost");
      
      try {
            Long counter = jedis.incr(REDIS_KEY);
            if (counter == 1) {
                // 第一次设置过期时间
                jedis.expire(REDIS_KEY, EXPIRE_TIME);
            }
            
            if (counter <= REQUEST_LIMIT) {
                return true; // 允许请求通过
            } else {
                return false; // 请求达到限流阈值,拒绝请求
            }
      } finally {
            jedis.close();
      }
    }

    public static void main(String[] args) {
      RedisRateLimiter rateLimiter = new RedisRateLimiter();
      for (int i = 0; i < 110; i++) {
            if (rateLimiter.allowRequest()) {
                System.out.println("Request Allowed");
            } else {
                System.out.println("Request Denied (Rate Limited)");
            }
      }
    }
}
滑动窗口算法

此方法的实现思路是:将请求都存入到ZSet集合中,在分数(score)中存储当前请求时间。然后再使用ZSet提供的range方法容易的获取到2个时间戳内的全部请求,通过获取的请求数和限流数进行比较并判断,从而实现限流。
具体实现代码如下:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;

import java.util.Set;

public class RedisSlidingWindowRateLimiter {

    private static final String ZSET_KEY = "request_timestamps";
    private static final int WINDOW_SIZE = 60; // 时间窗口大小(单位:秒)
    private static final int REQUEST_LIMIT = 100; // 限流阈值

    public boolean allowRequest() {
      Jedis jedis = new Jedis("localhost");
      long currentTimestamp = System.currentTimeMillis() / 1000;

      // 添加当前请求的时间戳到有序集合
      jedis.zadd(ZSET_KEY, currentTimestamp, String.valueOf(currentTimestamp));

      // 移除过期的请求时间戳,保持时间窗口内的请求
      long start = currentTimestamp - WINDOW_SIZE;
      long end = currentTimestamp;
      jedis.zremrangeByScore(ZSET_KEY, 0, start);

      // 查询当前时间窗口内的请求数量
      Set<Tuple> requestTimestamps = jedis.zrangeByScoreWithScores(ZSET_KEY, start, end);
      long requestCount = requestTimestamps.size();

      jedis.close();

      // 判断请求数量是否超过限流阈值
      return requestCount <= REQUEST_LIMIT;
    }

    public static void main(String[] args) {
      RedisSlidingWindowRateLimiter rateLimiter = new RedisSlidingWindowRateLimiter();

      for (int i = 0; i < 110; i++) {
            if (rateLimiter.allowRequest()) {
                System.out.println("Request Allowed");
            } else {
                System.out.println("Request Denied (Rate Limited)");
            }
      }
    }
}
漏桶算法

漏桶算法的实现思路是:使用一个固定容量的桶来存储请求,请求以固定的速率被处理。如果桶满了,新的请求将被丢弃。
令牌桶算法

令牌桶算法的实现思路是:有一个固定速率填充令牌的桶,令牌代表请求许可。当请求到达时,需要从桶中取出一个令牌,如果桶中有令牌则允许请求通过,否则拒绝。桶的容量是有限的,多余的令牌会被丢弃。
Redis限流的实践案例

在现实应用中,Redis限流可以应用于多个场景,比方API调用频率限制、用户举动监控、服务降级等。以下是一些实践案例:

[*]API调用频率限制:限制用户对特定API的调用频率,防止API滥用和过载。
[*]用户举动监控:监控用户的登录尝试、请求举动等,防止恶意攻击和滥用。
[*]服务降级:在体系负载过高时,通过限流计谋低沉非核心服务的请求量,确保核心服务的稳定性。
总结

Redis作为一个高性能的键值存储体系,在限流方面有着广泛的应用。通过使用不同的限流算法,如计数器算法、滑动窗口算法、漏桶算法和令牌桶算法,我们可以有用地控制请求流量,保护体系稳定性,优化用户体验,并低沉运维成本。每种算法都有其适用场景和优缺点,选择合适的限流计谋需要根据具体的业务需求和体系特点来决定。通过合理配置和优化,Redis可以帮助我们实现高效、稳定的限流办理方案。
在现实应用中,限流计谋的选择和实现需要综合思量业务特点、体系架构和性能要求。比方,在面对高并发、高流量的场景时,可能需要采用更复杂的限流算法和计谋,

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 如何用Redis实现限流?