前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >面试官:如何用Redis实现限流?

面试官:如何用Redis实现限流?

作者头像
用户11397231
发布2024-12-27 08:21:16
发布2024-12-27 08:21:16
11600
代码可运行
举报
文章被收录于专栏:算法算法
运行总次数:0
代码可运行

高并发系统有三大特征:限流、缓存和熔断,所以限流已经成为当下系统开发中必备的功能了。那么,什么是限流?如何实现限流?使用 Redis 能不能实现限流?接下来我们一起来看。

1. 什么是限流?

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

使用限流的好处:

  • 保护系统稳定性:过多的并发请求可能导致服务器内存耗尽、CPU 使用率饱和。
  • 防止资源滥用:确保有限的服务资源被合理公平地分配给所有用户。
  • 优化用户体验:对于网站和应用程序而言,如果任由高并发导致响应速度变慢,会影响所有用户的正常使用体验。
  • 保障安全:在网络层面,限流有助于防范 DoS/DDoS 攻击。
  • 运维成本控制:合理的限流措施可以帮助企业减少不必要的硬件投入。

2. 限流常见算法

限流的常见实现算法包括:

  • 计数器算法:将时间周期划分为固定大小的窗口,并在每个窗口内统计请求的数量。
  • 滑动窗口算法:改进了计数器算法的突刺问题,将时间窗口划分为多个小的时间段。
  • 漏桶算法:想象一个固定容量的桶,水(请求)以恒定速率流入桶中,同时桶底部有小孔让水以恒定速率流出。
  • 令牌桶算法:有一个固定速率填充令牌的桶,令牌代表请求许可。请求到达时,需要从桶中取出一个令牌。

3. 使用Redis实现限流

3.1 计数器算法

实现思路:使用一个计数器存储当前请求量,并设置一个过期时间,计数器在一定时间内自动清零。

具体实现代码

代码语言:javascript
代码运行次数:0
复制
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);
            }
            
            return counter <= REQUEST_LIMIT;
        } 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)");
            }
        }
    }
}

在上述代码中,每次请求会通过 allowRequest() 方法判断是否达到限流阈值,如果未达到则允许通过,并递增计数器的值,否则拒绝请求。同时,第一次设置计数器的过期时间,使得计数器在指定的时间内自动清零

3.2 滑动窗口算法

实现思路:将请求都存入到 ZSet 集合中,在分数(score)中存储当前请求时间。

具体实现代码

代码语言:javascript
代码运行次数:0
复制
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));
        jedis.zremrangeByScore(ZSET_KEY, 0, currentTimestamp - WINDOW_SIZE);

        Set<Tuple> requestTimestamps = jedis.zrangeByScoreWithScores(ZSET_KEY, currentTimestamp - WINDOW_SIZE, currentTimestamp);
        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 的滑动窗口限流算法可以有效控制单位时间内的请求流量,避免系统被过多请求压垮。

3.3 令牌桶算法

实现思路:使用定时任务给 Redis 中的 List 添加令牌,程序通过 List 提供的 leftPop 来获取令牌。

具体实现代码

代码语言:javascript
代码运行次数:0
复制
@Configuration
@EnableScheduling
public class StaticScheduleTask {
    @Autowired
    private RedisTemplate redisTemplate;

    @Scheduled(fixedRate = 1000)
    private void configureTasks() {
        redisTemplate.opsForList().rightPush("limit_list", UUID.randomUUID().toString());
    }
}

public boolean allowRequest() {
    Object result = redisTemplate.opsForList().leftPop("limit_list");
    return result != null;
}

在上述代码中,我们每次访问 allowRequest() 方法时,会尝试从 Redis 中获取一个令牌,如果拿到令牌了,那就说明没超出限制,可以继续执行,反之则不能执行。

总结

限流是高并发系统中维护稳定性和可用性的关键技术之一。通过合理选择和实现限流算法,可以有效控制请求流量,保护系统不受过载影响。Redis作为一种高性能的键值存储系统,提供了灵活的数据结构和操作,使其成为实现限流的理想选择。希望本文能帮助你更好地理解和应用限流技术。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 什么是限流?
  • 2. 限流常见算法
  • 3. 使用Redis实现限流
    • 3.1 计数器算法
    • 3.2 滑动窗口算法
    • 3.3 令牌桶算法
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档