What about repeated requests?

In Web development, the problem of repeated requests is inevitable.

Specific problems to specific analysis, let’s take a look, there are several types of repeated requests.

  1. First of all, there is an interface, suppose there is a fast hand speed, the server response can not keep up with his hand speed, the request comes over, this person does not rest, with inhuman speed click again, the server received a lot of the same request in a flash, a time the server is at a loss…
  2. Let’s say there’s an interface for sending SMS captcha, and there’s someone with the same speed as before, and there’s no interception or there’s only front-end interception, and this person bypassing your interception, and the messages go whizzing out and the money goes whizzing out.
  3. There is still an interface, but this interface has many requests at the same time. Normally, each user’s calls are isolated from each other, so there is no problem. However, if they operate on the same table, that is, the data operation intersection, then the data will become messy.

In fact, when it comes to repeated requests, everyone must think of Redis. Redis is generally used as a distributed lock to handle repeated requests and so on.

The general steps are as follows

  1. Once it’s in the interface, check it outRedisIs it specified inkeyThe data of
  2. If yes, return the message directly, if not, in theRedisPut in a piece of data and perform a specific business operation
  3. No further action is requiredRedisIn thiskeyThe data.

This key is typically set on a business basis.

To upgrade the

What happens when Redis encounters AOP, Annotation?

In fact, THE above way I also used for a long time, also did not feel where wrong. If an interface wanted to use distributed locks, I would write the same set as above. I even created a custom Live Template in Idea for laziness.

Programmers should have the noble quality of hating repetitive work. Repetitive work is a waste of life.

First of all,

In Java, annotations are best used, need to lock, add an annotation, how perfect. So define an annotation first.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Lock {

    / * * *@returnLock name */
    String name(a) default "";

    / * * *@returnLock time: 3 seconds */ by default
    int value(a) default 3;

    / * * *@returnWhether to force mode * true after a request must be locked for a period of time * false The lock is automatically removed after the method is executed, that is, the method can be accessed again */
    boolean hard(a) default false;

    / * * *@returnDistributed lock * true Distributed lock all users use the same lock, that is, the key is the same * false Different locks for each user, preventing a single user from submitting the lock repeatedly (submitting the lock too fast, for example, clicking a button too fast) */
    boolean distributed(a) default true;
}
Copy the code

The idea is beautiful, but the reality still needs their own efforts. With annotations, we need to work with methods that use annotations.

AOP

I actually thought of interceptors at first, but in the end I decided that AOP’s wrap enhancement was the best fit.

@Slf4j
@Aspect
@Component
public class RepeatRequestAspect {

    @Resource
    private RedisUtil redisUtil;

    /** ** section */
    @Pointcut("@annotation(top.lww0511.redislock.annotation.Lock)")
    public void pointcut(a) {}@Around("pointcut()")
    public Object around(ProceedingJoinPoint point) {
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        String ipAddr = IPUtils.getIpAddr(request);
        String servletPath = request.getServletPath();

        Method method = ((MethodSignature) point.getSignature()).getMethod();
        Lock lock = method.getAnnotation(Lock.class);
        int lockTime = lock.value();
        boolean hard = lock.hard();
        boolean distributed = lock.distributed();
        String lockKey = RedisKey.REQUEST_PREFIX + servletPath + (distributed ? "" : ("_" + ipAddr));
        lockKey = StringUtils.isEmpty(lock.name()) ? lockKey : RedisKey.REQUEST_PREFIX + lock.name() + (distributed ? "" : ("_" + ipAddr));
        log.info("RepeatRequestAspect_around_lockTime:{}, hard:{}, lockKey:{}", lockTime, hard, lockKey);
        String value = redisUtil.getValue(lockKey);
        if(! StringUtils.isEmpty(value)) { Assert.isTrue(false."Operation is too frequent, please take a break before operation!");
        }
        redisUtil.setValue(lockKey, lockKey, lockTime, TimeUnit.SECONDS);
        Object proceed = null;
        try {
            proceed = point.proceed();
        } catch (Throwable throwable) {
            log.error("RepeatRequestAspect_around_throwable:{}", throwable);
        } finally {
            if (!hard) {
                redisUtil.remove(lockKey);
            }
        }
        returnproceed; }}Copy the code

No complex code, just enforce all lock-annotated methods to get annotation values, such as Lock duration, whether mode is enforced, and whether distributed Lock.

  • In general, if I set the lock time to 10 seconds, then I can’t access it until 10 seconds later, even if I complete it in advance. Will not automatically delete as a lockRedisData in.
  • Distributed lock is easy to understand, set to distributed lock, then the same methodkeyIs the same, that is, a method can only be accessed by one user at a time, otherwise it is simply a defense against those with super fast hands.
  • As for the notesname(lock name), in fact, nothing useful, is capricious…

use

To configure Redis

spring.redis.host=127.0. 01.
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0
Copy the code

Join the rely on

<dependency>
    <groupId>top.lww0511</groupId>
    <artifactId>redis-lock</artifactId>
    <version>1.0.2</version>
</dependency>
Copy the code

Add the annotation Lock to the method

@Lock(value = 10, distributed = false, hard = true)
@GetMapping(value = "/hi", name = "log")
public HttpResult hello(a) {
    return HttpResult.success("Hello");
}
Copy the code
  • @Lock: Default: distributed. The lock duration is 3 seconds. This parameter is optional
  • @Lock(value = 10, distributed = false, hard = true): indicates that the system is not distributed, the lock duration is 10 seconds, and the mode is mandatory.

It’s not a special requirement, but an @lock is usually enough.

The effect

Non-distributed bindingIP, and is mandatory mode, although executed, the method is still not accessible.

When there is only one@Lockwhen

There is no bindingIPAnd although the lock time is 3 seconds, but the method is automatically released after execution.

The last

There are also utility classes, global exception interceptors, etc., and meta-INF/Spring.Factories.

GitHub address: github.com/LerDer/redi…

This is already published to the Maven central repository, so you can use it by simply adding dependencies. No need to package and publish to private server.

  • coordinates
<! -- https://mvnrepository.com/artifact/top.lww0511/redis-lock -->
<dependency>
    <groupId>top.lww0511</groupId>
    <artifactId>redis-lock</artifactId>
    <version>1.0.2</version>
</dependency>
Copy the code

Welcome to pay attention to my public number, learn together, progress together. Refueling 🤣

Search: Nanzhao Blog