“This is the 23rd day of my participation in the November Gwen Challenge. See details of the event: The Last Gwen Challenge 2021”.
In the face of more and more high concurrency scenarios, limiting the flow display is particularly important. Of course, there are many ways to achieve the flow limit, Redis has a very powerful function, I use Redis practice three kinds of implementation, can be relatively simple to achieve its way. Redis can do more than just stream limiting, it can also do statistics, people nearby, etc., which will be covered later.
The first: setnx operations based on Redis
When we use Redis distributed lock, we all know that we rely on setNx instruction. When CAS (Compare and swap) operates, we also set expire for the specified key. The main purpose of flow limiting is to make sure that within unit time, There are N and only N requests that can access my code. So setNx can be very easy to do this aspect of the function. For example, if we need to limit 20 requests within 10 seconds, we can set the expiration time of setNx to 10. When the number of setNx requests reaches 20, the flow limiting effect will be achieved. I won’t show you the code because it’s a little bit simpler. Of course, there are many disadvantages of this approach. For example, when the statistics are 1-10 seconds, it cannot be counted within 2-11 seconds. If we need to count M requests within N seconds, then we need to keep N keys in Redis and other problems
The second kind: data structure zset based on Redis
In fact, the most important thing involved in current limiting is sliding window. It is also mentioned above how 1-10 becomes 2-11. So it’s going to be +1 for both the starting value and the end value.
If we use the Redis list data structure, we can easily achieve this function
We can make requests into a Zset array, where each request comes in with a unique value that can be generated as a UUID, and score that can be represented as the current timestamp, because score is used to calculate how many requests are in the current timestamp. The Zset data structure also provides a range method that makes it easy to get the number of requests within the two timestamps
The following code
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 limiting traffic system.out.println (count); if (count ! = null &&count > 5) {return response. ok(" maximum 5 calls per minute "); } } redisTemplate.opsForZSet().add("limit",UUID.randomUUID().toString(),currentTime); Return response. ok(" Access successful "); }Copy the code
With the above code, you can achieve the effect of sliding Windows and guarantee up to M requests every N seconds, but the disadvantage is that the data structure of zset gets bigger and bigger. The implementation is relatively simple.
Third: token bucket algorithm based on Redis
When it comes to limiting traffic, the token bucket algorithm has to be mentioned.
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.
This means that every time we access a request, we can get a token from Redis. If we get a token, we are within the limit, and if we don’t get a token, we get the opposite result.
Based on the above ideas, we can easily achieve such code with Redis List data structure, just simple implementation
Rely on the leftPop of the List to get the 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 bucket "); } return Response.ok(articleDescription2); }Copy the code
Again, I rely on Java’s scheduled task to periodically push the token to the List. Of course, the token also needs to be unique, so I still use UUID to generate the token
// add UUID to token bucket, @scheduled (fixedDelay = 10_000,initialDelay = 0) public void setIntervalTimeTask(){ redisTemplate.opsForList().rightPush("limit_list",UUID.randomUUID().toString()); }Copy the code
To sum up, the code implementation is not very difficult to start, for these methods of limiting the flow we can add the above code in AOP or filter, used to achieve interface limiting, ultimately protect your site. Redis has many other uses, not just for caching and distributed locking. His data structures are not just String, Hash, List, Set, Zset. If you’re interested, follow up on his GeoHash algorithm; BitMap, HLL and Bloom filter data (added after Redis4.0, you can install Redislabs/Rebloom directly with Docker) structure.