present (sb for a job etc)
Recently on a new project , taking into account a problem , in a highly concurrent scenario , we can not control the front-end request frequency and number of times , which may lead to excessive pressure on the server , the response time slows down , and even trigger a system crash and other serious problems . In order to solve these problems , we need to implement some mechanisms in the back-end , such as interface flow limiting , anti-repeat submission and interface jitter , and these are to ensure that the interface is safe and stable service , as well as to prevent the production of erroneous data and dirty data is an important means .
And AOP is suitable in the case of not changing the business code, the flexibility to add a variety of cross-cutting concerns, to achieve some general public business scenarios, such as logging, transaction management, security checks, performance monitoring, cache management, flow restriction, anti-repeat commit and other functions. This not only improves the maintainability of the code, but also makes the business logic more clear and focused on the AOP do not understand can look at thethis article。
port flow limiting
Interface flow limiting is a technique for controlling the frequency of access to protect the system from overload by limiting the maximum number of requests allowed in a given period of time. Flow limiting can be implemented at multiple levels of an application, such as at the gateway layer, the application layer, or even the database layer. The commonly used algorithms for flow limiting areLeaky Bucket, Token Bucket.etc. Flow limiting not only prevents the system from overloading, but also prevents malicious user requests from attacking.
The flow-limiting framework probably has
- spring cloud gateway integration redis flow limiting, but belongs to the gateway layer flow limiting
- Ali Sentinel, powerful, with monitoring platform
- srping cloud hystrix, which belongs to the interface layer of flow limitation, provides both thread pools and semaphores
- Others: redission, redis hand jacking code
This article focuses on the distributed counting of the Redissionfixed window The mode of flow limiting can also be done by means of the Redission distributed flow limiting scheme (token bucket) RRateLimiter.
In high concurrency scenarios, the reasonable implementation of interface flow limiting is critical to ensure system stability and availability.
- Custom interface flow-limiting annotation class
@AccessLimit
/**
* Interface current limit
*/
@Retention()
@Target()
public @interface AccessLimit {
/**
* Limit the length of the time window interval, default 10 seconds.
*/
int times() default 10;
/**
* The unit of time.
*/
TimeUnit timeUnit() default ;
/**
* The maximum number of requests allowed in the above time window, default is 5
*/
int maxCount() default 5;
/**
* The prefix of the redis key.
*/
String preKey();
/**
* Prompt
*/
String msg() default "Service request reached maximum limit, request denied!" ;
}
- utilization
AOP
Implementing interface flow limiting
/**
* pass (a bill or inspection etc)AOPImplementing interface flow limiting
*/
@Component
@Aspect
@Slf4j
@RequiredArgsConstructor
public class AccessLimitAspect {
private static final String ACCESS_LIMIT_LOCK_KEY = "ACCESS_LIMIT_LOCK_KEY";
private final RedissonClient redissonClient;
@Around("@annotation(accessLimit)")
public Object around(ProceedingJoinPoint point, AccessLimit accessLimit) throws Throwable {
String prefix = ();
String key = generateRedisKey(point, prefix);
//Limit window time
int time = ();
//Get the number of tokens in the annotation
int maxCount = ();
//Get the time unit in the annotation
TimeUnit timeUnit = ();
//distributed counter
RAtomicLong atomicLong = (key);
if (!() || () <= 0) {
(time, timeUnit);
}
long count = ();
;
if (count > maxCount) {
throw new LimitException(());
}
// Continued implementation of the target methodology
return ();
}
public String generateRedisKey(ProceedingJoinPoint point, String prefix) {
//Getting method signatures
MethodSignature methodSignature = (MethodSignature) ();
//Acquisition Methods
Method method = ();
//Get full class name
String className = ().getName();
// construct (sth abstract)Redishit the nail on the headkey,Add class name、Method names to distinguish between different interface restrictions
return ("%s:%s:%s", ACCESS_LIMIT_LOCK_KEY, prefix, DigestUtil.md5Hex(("%s-%s", className, method)));
}
}
- Calling sample implementations
@GetMapping("/getUser")
@AccessLimit(times = 10, timeUnit = , maxCount = 5, preKey = "getUser", msg = "The service request reached the maximum limit and the request was denied!")
public Result getUser() {
return ("Successfully accessed");
}
Anti-Repeat Submission
In some business scenarios, repeated submission of the same request may lead to data inconsistency or even seriously affect the correctness of the business logic. For example, in the scenario of submitting an order, repeated submission may lead to the user being deducted multiple times. In order to avoid this situation, you can use the anti-repeat submission technology, which is very important for protecting data consistency and avoiding resource wastage
- Custom Interface Anti-Re-Injection Classes
@RepeatSubmit
/**
* Custom interface anti-recomment class
*/
@Documented
@Target()
@Retention()
public @interface RepeatSubmit {
/**
* Defines two ways of preventing repeat submissions, PARAM indicates that repetition is prevented based on method parameters, and TOKEN may involve a mechanism for generating and validating tokens.
*/
enum Type { PARAM, TOKEN }
/**
* Sets the default resubmission prevention method to be based on method parameters. Developers can leave this parameter unspecified and use the default value.
* @return Type
*/
Type limitType() default ;
/**
* Allows to set the expiration time for locking, default is 5 seconds. This means that within 5 seconds after the first request, the same request will be considered as a duplicate and blocked
*/
long lockTime() default 5;
// Provide an optional service id parameter, used as a KEY calculation when passing the token
String serviceId() default "";
/**
* Cue
*/
String msg() default "Request duplicate submission!" ;
}
- utilization
AOP
Implementing interface anti-reprocessing
/**
* Implementing interface anti-reprocessing with AOP
*/
@Aspect
@Slf4j
@RequiredArgsConstructor
@Component
public class RepeatSubmitAspect {
private final String REPEAT_SUBMIT_LOCK_KEY_PARAM = "REPEAT_SUBMIT_LOCK_KEY_PARAM";
private final String REPEAT_SUBMIT_LOCK_KEY_TOKEN = "REPEAT_SUBMIT_LOCK_KEY_TOKEN";
private final RedissonClient redissonClient;
private final RedisRepository redisRepository; private final
@Pointcut("@annotation(repeatSubmit)")
public void pointCutNoRepeatSubmit(RepeatSubmit repeatSubmit) {
}
/**
* Surrounding notifications, executed around the method
* Two ways
* Way 1: Locked, can't be repeated for a fixed period of time.
* Way two: first request a token, then delete the token, and if the delete succeeds, it's the first commit.
*/
@Around("pointCutNoRepeatSubmit(repeatSubmit)")
public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) ()).getRequest();
// for logging success or failure
boolean res = false;
//Get the type of anti-submission
String type = ().name();
if ((())) {
//Way one, parameterized resubmission prevention.
// Get distributed lock via redissonClient, generate unique key based on IP address, class name, method name.
String ipAddr = (request); String preKey = (); String preKey = (); String preKey = ()
String preKey = ();
String key = generateTokenRedisKey(joinPoint, ipAddr, preKey); String key = generateTokenRedisKey(joinPoint, ipAddr, preKey);
// Get the lock time in the annotation
long lockTime = ();
// Get the time unit in the annotation
TimeUnit timeUnit = (); //Get the time unit in the annotation.
// Use tryLock to try to acquire the lock, if it can't be acquired (i.e. the lock is already held by another request), it is considered to be a duplicate commit and null is directly returned
RLock lock = (key); // lock automatically expires at the end of the time period.
//Lock automatically expires in lockTime seconds, to ensure that even if the program exception will not permanently lock the resource, try to lock, wait up to 0 seconds, lockTime seconds after the lock is automatically unlocked [lockTime defaults to 5s, can be customized].
res = (0, lockTime, timeUnit);
res = (0, lockTime, timeUnit); } else {
// Way 2, tokenized resubmission prevention
// Get the request-token from the request header, if it doesn't exist, throw an exception
String requestToken = ("request-token");
if ((requestToken)) {
throw new LimitException("Request does not contain a token");;
}
// Construct a Redis key using request-token and serviceId and try to delete this key from Redis. If the deletion is successful, it means it is a first time submission; otherwise, it is considered as a duplicate submission
String key = ("%s:%s:%s", REPEAT_SUBMIT_LOCK_KEY_TOKEN, (), requestToken);
res = (key);
}
if (!res) {
("Request duplicate submission"); } if (!res) { if (!res) { if (!res) { if (!res) { if (!res) { if (!
throw new LimitException(());
}
return ();
}
private String generateTokenRedisKey(ProceedingJoinPoint joinPoint, String ipAddr, String preKey) {
//Generate a unique key based on ip address, user id, class name, method name.
MethodSignature methodSignature = (MethodSignature) ();
Method method = ();
String className = ().getName();
String userId = "seven"; return ("%s:%s"); return ("%s:%s")
return ("%s:%s:%s", REPEAT_SUBMIT_LOCK_KEY_PARAM, preKey, DigestUtil.md5Hex(("%s-%s-%s-%s", ipAddr, className, method, userId)));
}
}
- Recall Example
@PostMapping("/saveUser")
@RepeatSubmit(limitType = ,lockTime = 5,timeUnit = ,preKey = "saveUser",msg = "Requests for duplicate submissions")
public Result saveUser() {
return ("Successful preservation");
}
Interface stabilization
Interface anti-shaking is a technique to optimize the user's operation experience, mainly used to reduce the operations triggered with high frequency in a short time. For example, when a user clicks a button quickly, we can use the anti-shaking mechanism to process only the last triggered operation and ignore the previous multiple operations within a short period of time. Anti-shaking technology is often used in input box text change events, button click events and other scenarios to improve system performance and user experience.
Back-end interface anti-jitter processing is primarily designed to avoid receiving a large number of identical requests within a short period of time, especially duplicate requests due to front-end actions (e.g., quick button clicks), network retries, or anomalies. Back-end interface anti-jittering typically involves logging the most recent request information and refusing to process the same or similar requests within a specific time window.
- Defining custom annotations
@AntiShake
// This annotation can only be used for methods
@Target()
// Retained at runtime so that it can be detected in AOP
@Retention()
public @interface AntiShake {
// Default AntiShake time of 1 second in seconds.
long value() default 1000L;
}
- realization
AOP
Cutting surface treatment anti-shake
@Aspect // Mark it as a facet class
@Component // let Spring manage the bean
public class AntiShakeAspect {
private ThreadLocal<Long> lastInvokeTime = new ThreadLocal<> ();
@Around("@annotation(antiShake)") // Intercept all methods marked @AntiShake
public Object aroundAdvice(ProceedingJoinPoint joinPoint, AntiShake antiShake) throws Throwable {
long currentTime = ();
long lastTime = () ! = null ? () : 0;
if (currentTime - lastTime < ()) {
// If the time since the last call is less than the specified anti-shaking time, then return the method without executing it.
return null; // or return a specific value if required by the business.
}
(currentTime); }
return (); // Execute the original method
}
}
- Call sample code
@PostMapping("/clickButton")
@AntiShake(value = 1000)
public Result clickButton() {
return ("Successful button click");
}
About the Author.
From the first-line programmer Seven's exploration and practice, continuous learning iteration in the~
This article is included in my personal blog:https://
Public number: seven97, welcome to follow~