Redis implements current limiting in three ways

Keywords: Database Redis

In the face of more and more high concurrency scenarios, the display of current limit is particularly important.

Of course, there are many ways to implement current limiting. Redis has very powerful functions. I have practiced three ways with redis, which can be implemented in a relatively simple way. Redis can not only limit current, but also perform data statistics, people nearby and other functions. These may be added later.

First: Redis based setnx operation

When we use Redis distributed locks, we all know that we rely on setnx instructions. During CAS (Compare and swap) operation, we also set expiration practice for the specified key. Our main purpose in current limiting is to have and only N requests can access my code program per unit time. So it's easy to do this with setnx.

For example, if we need to limit 20 requests within 10 seconds, we can set the expiration time of 10 in setnx. When the number of requested setnx reaches 20, the current limiting effect is achieved. The code is relatively simple, so I won't show it.

Of course, there are many disadvantages of this method. For example, when counting 1-10 seconds, we can't count within 2-11 seconds. If we need to count M requests in N seconds, we need to keep n key s in Redis, and so on

Second: Redis based data structure zset

In fact, the most important thing involved in current limiting is the sliding window. It is also mentioned above how 1-10 becomes 2-11. In fact, the start value and end value are + 1 respectively.

If we use Redis's list data structure, we can easily realize this function

We can make the request into a zset array. When each request comes in, the value remains unique and can be generated by UUID, and the score can be expressed by the current timestamp, because the score can be used to calculate the number of requests within the current timestamp. The zset data structure also provides a range method, so that we can easily obtain the number of requests within two timestamps

The code is as follows

public Response limitFlow(){
        Long currentTime = new Date().getTime();
        System.out.println(currentTime);
        if(redisTemplate.hasKey("limit")) {
            Integer count = redisTemplate.opsForZSet().rangeByScore("limit", currentTime -  intervalTime, currentTime).size();        //  intervalTime is the time of current limiting  
            System.out.println(count);
            if (count != null && count > 5) {
                return Response.ok("Up to 5 visits per minute");
            }
        }
        redisTemplate.opsForZSet().add("limit",UUID.randomUUID().toString(),currentTime);
        return Response.ok("Access successful");
    }

Through the above code, the sliding window effect can be achieved, and at most M requests per N seconds can be guaranteed. The disadvantage is that the data structure of zset will become larger and larger. The implementation method is relatively simple.

Third: Redis based token bucket algorithm

When it comes to current limiting, we have to mention the token bucket algorithm.

The token bucket algorithm mentions the input rate and output rate. When the output rate is greater than the input rate, the traffic limit is exceeded.

That is, every time we access a request, we can get a token from Redis. If we get the token, it means that the limit is not exceeded. If we can't get it, the result is the opposite.

Relying on the above ideas, we can easily implement such code in combination with Redis's List data structure, which is just a simple implementation

Rely on the leftPop of the List to get the token

//  Output token
public Response limitFlow2(Long id){
        Object result = redisTemplate.opsForList().leftPop("limit_list");
        if(result == null){
            return Response.ok("There are no tokens in the current token bucket");
        }
        return Response.ok(articleDescription2);
    }

Then, depending on the timing task of Java, the rightPush token is regularly sent to the List. Of course, the token also needs to be unique, so I still use UUID to generate it here

//  The UUID is added to the token bucket at a rate of 10S to ensure uniqueness
    @Scheduled(fixedDelay = 10_000,initialDelay = 0)
    public void setIntervalTimeTask(){
        redisTemplate.opsForList().rightPush("limit_list",UUID.randomUUID().toString());
    }

To sum up, it is not difficult to start the code implementation. For these current limiting methods, we can add the above code to AOP or filter to limit the current of the interface and finally protect your website.

Redis actually has many other uses. Its role is not only cache, but also distributed lock. Its data structure is not just String, Hash, List, Set and Zset. If you are interested, you can learn about his GeoHash algorithm later; BitMap, HLL and bloom filter data (added after Redis4.0, redislabs / rebloom can be directly installed with Docker) structure.

Posted by markbett on Thu, 04 Nov 2021 22:48:13 -0700