Three ways Redis can realize current limiting

Posted by weiwei on Mon, 28 Feb 2022 11:18:28 +0100

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 do data statistics, people nearby and other functions. These may be added later.

First: Redis based setnx operation

When we use Redis's distributed lock, 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 in unit time. Therefore, it is easy to achieve this function by relying on 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.

For the specific usage of setnx, please refer to my other blog A series of problems caused by Redis distributed lock under RedisTemplate

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.

The second is the Redis based data structure zset

In fact, the most important thing involved in current limiting is the sliding window, which also mentioned 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

zset is an upgraded version of set. It adds an order attribute on the basis of set. This attribute can be specified when adding and modifying elements. After each assignment, zset will automatically adjust the order according to the new value. You can set the sorting weight of the value of the specified key. It has many application ranking modules.

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 how many requests are there 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("Visit successful");
    }

Through the above code, the effect of sliding window can be achieved, and at most M requests per N seconds can be guaranteed. The disadvantage is that the data structure of zset will be 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 refers to the input rate and output rate. When the output rate is greater than the input rate, the traffic limit is exceeded.

In other words, 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 achieve such code in combination with Redis's List data structure, which is just a simple implementation

Rely on the leftPop of the List to obtain the token

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

Then rely on the timing task of Java to regularly push the rightPush token into 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 the 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, the code implementation is not very difficult. 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.

Reprint: Three ways redis can realize current limiting_ lmx125254 blog - CSDN blog_ Redis current limiting

Topics: Database Redis Cache