it编程 > 数据库 > Redis

Redis实现分布式限流的几种方法

30人参与 2026-01-11 Redis

使用redis实现分布式限流是一种常见且有效的方法,可以防止系统过载并确保公平的资源分配。redis的高性能和丰富的数据结构使其成为实现分布式限流的理想选择。常见的限流算法包括固定窗口计数、滑动窗口计数和令牌桶算法。

1. 固定窗口计数算法

固定窗口计数算法将时间划分为固定长度的窗口,并在每个窗口内计数请求的数量。

示例代码

以下示例展示了如何使用redis实现固定窗口计数算法的分布式限流:

import redis.clients.jedis.jedis;

public class fixedwindowratelimiter {

    private jedis jedis;
    private int maxrequests;
    private int windowsize; // 窗口大小,单位为秒

    public fixedwindowratelimiter(string host, int port, int maxrequests, int windowsize) {
        this.jedis = new jedis(host, port);
        this.maxrequests = maxrequests;
        this.windowsize = windowsize;
    }

    public boolean isallowed(string clientid) {
        string key = "rate_limiter:" + clientid;
        long currentwindow = system.currenttimemillis() / 1000 / windowsize;
        string windowkey = key + ":" + currentwindow;

        long requestcount = jedis.incr(windowkey);
        if (requestcount == 1) {
            jedis.expire(windowkey, windowsize);
        }

        return requestcount <= maxrequests;
    }

    public void close() {
        jedis.close();
    }

    public static void main(string[] args) {
        fixedwindowratelimiter ratelimiter = new fixedwindowratelimiter("localhost", 6379, 5, 60);

        for (int i = 0; i < 10; i++) {
            boolean allowed = ratelimiter.isallowed("client1");
            system.out.println("request " + (i + 1) + " allowed: " + allowed);
            try {
                thread.sleep(500); // 模拟请求间隔
            } catch (interruptedexception e) {
                e.printstacktrace();
            }
        }

        ratelimiter.close();
    }
}

2. 滑动窗口计数算法

滑动窗口计数算法通过记录多个小窗口内的请求数,计算滑动窗口内的总请求数。

示例代码

以下示例展示了如何使用redis实现滑动窗口计数算法的分布式限流:

import redis.clients.jedis.jedis;
import redis.clients.jedis.transaction;

import java.util.list;

public class slidingwindowratelimiter {

    private jedis jedis;
    private int maxrequests;
    private int windowsize; // 窗口大小,单位为秒
    private int interval; // 时间间隔,单位为秒

    public slidingwindowratelimiter(string host, int port, int maxrequests, int windowsize, int interval) {
        this.jedis = new jedis(host, port);
        this.maxrequests = maxrequests;
        this.windowsize = windowsize;
        this.interval = interval;
    }

    public boolean isallowed(string clientid) {
        string key = "rate_limiter:" + clientid;
        long currenttime = system.currenttimemillis() / 1000;
        long windowstart = currenttime - windowsize;

        transaction transaction = jedis.multi();
        transaction.zadd(key, currenttime, string.valueof(currenttime));
        transaction.zremrangebyscore(key, 0, windowstart);
        transaction.zcard(key);
        transaction.expire(key, windowsize + interval);

        list<object> results = transaction.exec();
        long requestcount = (long) results.get(2);

        return requestcount <= maxrequests;
    }

    public void close() {
        jedis.close();
    }

    public static void main(string[] args) {
        slidingwindowratelimiter ratelimiter = new slidingwindowratelimiter("localhost", 6379, 5, 60, 1);

        for (int i = 0; i < 10; i++) {
            boolean allowed = ratelimiter.isallowed("client1");
            system.out.println("request " + (i + 1) + " allowed: " + allowed);
            try {
                thread.sleep(500); // 模拟请求间隔
            } catch (interruptedexception e) {
                e.printstacktrace();
            }
        }

        ratelimiter.close();
    }
}

3. 令牌桶算法

令牌桶算法通过生成令牌来控制请求的速率。每次请求需要消耗一个令牌,如果桶中没有令牌,则请求被拒绝。

示例代码

以下示例展示了如何使用redis实现令牌桶算法的分布式限流:

import redis.clients.jedis.jedis;

public class tokenbucketratelimiter {

    private jedis jedis;
    private int maxtokens;
    private int refillrate; // 令牌生成速率,单位为令牌/秒

    public tokenbucketratelimiter(string host, int port, int maxtokens, int refillrate) {
        this.jedis = new jedis(host, port);
        this.maxtokens = maxtokens;
        this.refillrate = refillrate;
    }

    public boolean isallowed(string clientid) {
        string key = "rate_limiter:" + clientid;
        long currenttime = system.currenttimemillis() / 1000;
        long lastrefilltime = jedis.hget(key, "lastrefilltime") == null ?
                0 : long.parselong(jedis.hget(key, "lastrefilltime"));
        int tokens = jedis.hget(key, "tokens") == null ?
                maxtokens : integer.parseint(jedis.hget(key, "tokens"));

        long tokenstoadd = (currenttime - lastrefilltime) * refillrate;
        tokens = math.min(maxtokens, tokens + (int) tokenstoadd);
        lastrefilltime = currenttime;

        if (tokens > 0) {
            jedis.hset(key, "tokens", string.valueof(tokens - 1));
            jedis.hset(key, "lastrefilltime", string.valueof(lastrefilltime));
            return true;
        } else {
            jedis.hset(key, "tokens", string.valueof(tokens));
            jedis.hset(key, "lastrefilltime", string.valueof(lastrefilltime));
            return false;
        }
    }

    public void close() {
        jedis.close();
    }

    public static void main(string[] args) {
        tokenbucketratelimiter ratelimiter = new tokenbucketratelimiter("localhost", 6379, 5, 1);

        for (int i = 0; i < 10; i++) {
            boolean allowed = ratelimiter.isallowed("client1");
            system.out.println("request " + (i + 1) + " allowed: " + allowed);
            try {
                thread.sleep(500); // 模拟请求间隔
            } catch (interruptedexception e) {
                e.printstacktrace();
            }
        }

        ratelimiter.close();
    }
}

4. 令牌桶算法与lua脚本

为了确保限流操作的原子性,可以使用redis的lua脚本。以下示例展示了如何结合lua脚本和令牌桶算法来实现分布式限流。

lua脚本

保存为token_bucket.lua

local key = keys[1]
local maxtokens = tonumber(argv[1])
local refillrate = tonumber(argv[2])
local currenttime = tonumber(argv[3])

local tokens = tonumber(redis.call("hget", key, "tokens") or maxtokens)
local lastrefilltime = tonumber(redis.call("hget", key, "lastrefilltime") or 0)

local tokenstoadd = math.floor((currenttime - lastrefilltime) * refillrate)
tokens = math.min(maxtokens, tokens + tokenstoadd)
lastrefilltime = currenttime

if tokens > 0 then
    redis.call("hset", key, "tokens", tokens - 1)
    redis.call("hset", key, "lastrefilltime", lastrefilltime)
    return 1
else
    redis.call("hset", key, "tokens", tokens)
    redis.call("hset", key, "lastrefilltime", lastrefilltime)
    return 0
end

java代码

import redis.clients.jedis.jedis;
import redis.clients.jedis.jedispool;

import java.io.ioexception;
import java.nio.file.files;
import java.nio.file.paths;

public class tokenbucketratelimiterwithlua {

    private jedispool jedispool;
    private string luascript;
    private string scriptsha;

    public tokenbucketratelimiterwithlua(string host, int port, string scriptpath) throws ioexception {
        this.jedispool = new jedispool(host, port);
        this.luascript = new string(files.readallbytes(paths.get(scriptpath)));
        try (jedis jedis = jedispool.getresource()) {
            this.scriptsha = jedis.scriptload(luascript);
        }
    }

    public boolean isallowed(string clientid, int maxtokens, int refillrate) {
        string key = "rate_limiter:" + clientid;
        long currenttime = system.currenttimemillis() / 1000;


到此这篇关于redis实现分布式限流的几种方法 的文章就介绍到这了,更多相关redis 分布式限流内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

您想发表意见!!点此发布评论

推荐阅读

Redis的主从同步问题的一些解决方法

01-11

nginx+rtmp实现直播完整流程

01-11

基于Redis实现登录功能思路详解(手机号+验证码)

01-08

Redis中ziplist与quicklist解析与对比小结

01-07

Redis热点Key独立集群实现方案(核心思路)

01-07

Redis安全高效删除包含特定模式的所有键值对的完整方案

01-13

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论