preface
What is increment?
The INCR command of Redis increments the numeric value stored in the key. If the key does not exist, the value of the key is initialized to 0 before the INCR operation is performed. An error is returned if the value contains the wrong type, or if a value of type string cannot be represented as a number. The value of this operation is limited to 64-bit (bit) signed numeric representations. [1]
Usage scenarios
- The ID is automatically generated and meets the concurrency requirements
- Use the counting feature to prevent duplicate requests
- That records the number of user clicks
- Traffic limiting in concurrent request scenarios
- .
In actual combat
The development environment
- Spring – the boot version 2.1.6. RELEASE
- Spring – the boot – starter – data – redis versions 2.1.6. RELEASE
The source code section
// Interface ValueOperations code
/ * * * will {@codeInteger values stored as string values under key} plus one. * *@param key must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.1
* @see <a href="http://redis.io/commands/incr">Redis Documentation: INCR</a>
*/
@Nullable
Long increment(K key);
/ * * * {@codeString values under key} follow {@codeDelta} integer value to increment. * *@param key must not be {@literal null}.
* @paramDelta increasing value *@return {@literal null} when used in pipeline / transaction.
* @see <a href="http://redis.io/commands/incrby">Redis Documentation: INCRBY</a>
*/
@Nullable
Long increment(K key, long delta);
/ * * * {@codeString values under key} follow {@codeThe float value of delta} to increment. * *@param key must not be {@literal null}.
* @param delta
* @return {@literal null} when used in pipeline / transaction.
* @see <a href="http://redis.io/commands/incrbyfloat">Redis Documentation: INCRBYFLOAT</a>
*/
@Nullable
Double increment(K key, double delta);
/ / class DefaultValueOperations implementation
@Override
public Long increment(K key) {
byte[] rawKey = rawKey(key);
return execute(connection -> connection.incr(rawKey), true);
}
@Override
public Long increment(K key, long delta) {
byte[] rawKey = rawKey(key);
return execute(connection -> connection.incrBy(rawKey, delta), true);
}
@Override
public Double increment(K key, double delta) {
byte[] rawKey = rawKey(key);
return execute(connection -> connection.incrBy(rawKey, delta), true);
}
// If you are interested, you can browse the source code
Copy the code
The business requirements
Suppose you need to limit the number of calls to the user generated TWO-DIMENSIONAL code interface, at a certain point in time can only be called how many times, more than the number of times will no longer go to generate two-dimensional code logic but directly prompt the user “frequent access” or “times has reached the upper limit”.
Code implementation
Testlimitcontroller.java is a simple implementation of how to limit traffic through the counter, there is no implementation of specific two-dimensional code generation logic
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@Slf4j
@RestController
public class TestLimitController {
@Autowired
private RedisTemplate<String, String> stringRedisTemplate;
@GetMapping(path = "/getqrcode")
public String getQRCode(String username, HttpServletResponse response) {
// Requirements: suppose you need to limit the number of calls to the user generated TWO-DIMENSIONAL code interface, how many times can only be called at a certain time, after the number of times will no longer go to generate two-dimensional code logic but directly prompt the user "access frequent" or "times has reached the upper limit".
// The current thread name
String currentThreadName = Thread.currentThread().getName();
// Count the number of times a user accesses an interface according to the user name
String key = username + "_getQRCode";
// Threshold: upper limit for the number of times a user invokes an interface
long threshold = 2;
/ / 30 seconds
final long timeout = 30;
final TimeUnit unit = TimeUnit.SECONDS;
Long increment = stringRedisTemplate.opsForValue().increment(key);
log.info("{} counter value: {}", currentThreadName, increment);
if (null! = increment) {if (increment > threshold) {
log.info("{} count has reached the upper limit", currentThreadName);
// To view the status of the result tree during jMeter testing
try {
response.sendError(HttpStatus.LOCKED.value(), "The number has reached its limit.");
} catch (IOException e) {
e.printStackTrace();
}
return "The number has reached its limit.";
} else {
if (1L == increment) {
// Set the expiration time of the key. When the key expires, the maximum number of times returns to zero
Boolean expire = stringRedisTemplate.expire(key, timeout, unit);
}
log.info("{} TWO-DIMENSIONAL code generated successfully", currentThreadName);
return "Two-dimensional code generated successfully";
}
}
log.info({} increment is NULL, currentThreadName);
// To view the status of the result tree during jMeter testing
try {
response.sendError(HttpStatus.LOCKED.value(), Increment is NULL);
} catch (IOException e) {
e.printStackTrace();
}
return "Qr code generation failed"; }}Copy the code
Code the measured
To write a code for yourself whether can stand the test, I am here to use the test tool is jmeter [2] to generate qr code to simulate the concurrent users call interface, assumption that we set a user can only be generated within 30 seconds twice qr code, should be to achieve the result of the interface of feedback after 2 times should prompt the user number has reached upper limit; The verification results are as follows:
Test scenario: JMeter simulates a user who has 10 threads open at the same time to invoke the interface
Jmeter test result tree:
View background logs. Only two threads are generated successfully
The markup stored in Redis for the user to access the getQRCode interface
Of course, this test only used 10 threads to simulate, and the test results also met the expected results. However, it does not represent the effect of the real production environment. It is suggested that students who have read this article should increase testing efforts if they want to go to the production environment, so as to avoid problems caused by insufficient testing after going online.
conclusion
To sum up, in some systems with limits on high concurrent requests, we can use Redis increment and EXPIRE to implement traffic limiting for interfaces. When users frequently request, we can protect the background system by limiting the number of times, so as to prevent system crash caused by excessive traffic impact.
Problems discussed
Will there be a problem with using Redis increment and EXPIRE to implement high concurrency limiting?
What could go wrong? Why do these problems arise?
How can problems be solved or avoided?
What else in the Java technology stack is available for limiting traffic?
Haha, see here the students must be technical excellence, may we discuss in the comments section