SpringBoot custom annotation + AOP + redis realizes anti-interface idempotent repeated submission, from concept to actual combat

This article is an exclusive creation of Qianfeng Education Technology Group, more technical knowledge and dry goods, please pay attention to continue to follow~

insert image description here

Interface idempotency is a very important concept in web development, it can ensure that calling the same interface multiple times will not affect the result. If you want to learn more about interface idempotence, this article is a good place to start.

In web development, we often need to prevent users from repeatedly submitting an operation, especially some operations that require data consistency, such as payment. Interface idempotence is a solution to this problem.

Interface idempotence means that no matter how many times the same interface is called, the final result is consistent. If the interface is not idempotent, multiple calls may lead to data inconsistency and even inexplicable errors. **

So, how to achieve interface idempotency?

In this article, Xiaoyue will introduce an implementation solution to you, namely: use SpringBoot custom annotations + AOP + redis to prevent repeated submission of idempotent interfaces.

1. Concept analysis

1.1 Interface idempotence

Interface idempotence means that multiple calls to the same interface have the same final result. This means that no matter how many times the interface is called, the final result should be the same. This is because the idempotence of the interface ensures that calling the interface multiple times will not affect the result.

In web development, it is very important to ensure the idempotence of interfaces.

For example, suppose we have an interface to modify user information, then the interface should be idempotent. If the user calls this interface multiple times, the final result should be consistent, that is, the user information is successfully modified. If the interface is not idempotent, multiple calls may lead to data inconsistency and even inexplicable errors.

In order to achieve the idempotency of the interface, we can use some technical means, such as using Token or storing the processing status of the request on the server side. These technical means can ensure that the same request will only be processed once, thus ensuring the idempotency of the interface.

In short, interface idempotence is a very important concept in web development, which can ensure that calling the same interface multiple times will not affect the result. Therefore, we need to pay attention to ensuring the idempotency of the interface during the development process to ensure system stability and data consistency.

1.2 Anti-duplicate submission

Anti-repetitive submission means that the system must be able to recognize that the user has repeatedly submitted an operation, and will not perform the operation again. This is to avoid problems caused by data inconsistency and repeated operations.

In this article, we use custom annotations @Idempotent、AOP和Redisto implement anti-interface idempotent duplication submission.

When a request is processed, we will store the processing status of the request in Redis and set an expiration time to ensure that the memory space of Redis will not be occupied all the time. Look at the sample code:

@RestController
public class DemoController {
    
    

    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("/demo")
    @Idempotent(expire = 60)
    public String demo(@RequestParam("id") Long id) {
    
    
        if (redisTemplate.hasKey("demo:" + id)) {
    
    
            return "请勿重复提交";
        }
        // 处理请求
        redisTemplate.opsForValue().set("demo:" + id, "1", 60, TimeUnit.SECONDS);
        return "success";
    }
}

In the above code, we use a custom annotation on the demo method @Idempotentand set the expiration time to 60 seconds. When a request is processed, we will store the processing status of the request in Redis to ensure that the operation will not be performed again within 60 seconds. If the user submits the operation repeatedly, the system will return a prompt not to submit repeatedly. In this way, problems caused by repeated submission of interfaces can be effectively avoided.

It should be noted that in order to prevent multiple requests from reaching the server at the same time, resulting in multiple simultaneous processing, we need to add a lock in Redis, which can be achieved by using the setnx command of Redis or distributed locks. In addition, in order to ensure idempotence, we need to ensure that the request is idempotent, that is, the results of multiple requests are consistent. If the request is not idempotent, then we need to deduplicate the request to ensure that only one request is processed.

2. Implementation plan

2.1 Custom annotations

In order to achieve the idempotency of the interface, we need to define a custom annotation first. The function of the annotation is to mark whether a method supports idempotence. If idempotence is supported, special handling is required for this method so that multiple calls to this method will not affect the result.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
    
    
}

2.2 AOP aspect

We can use AOP to determine whether a method is marked with @Idempotentannotations. If the annotation is marked, then special handling is required for the method to achieve idempotency.

@Aspect
@Component
public class IdempotentAspect {
    
    

    private final RedisTemplate redisTemplate;

    @Autowired
    public IdempotentAspect(RedisTemplate redisTemplate) {
    
    
        this.redisTemplate = redisTemplate;
    }

    @Around("@annotation(com.example.demo.annotation.Idempotent)")
    public Object idempotent(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        // 获取请求参数
        Object[] args = joinPoint.getArgs();
        // 获取请求方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取注解信息
        Idempotent idempotent = method.getAnnotation(Idempotent.class);
        String key = getKey(joinPoint);
        // 判断是否已经请求过
        if (redisTemplate.hasKey(key)) {
    
    
            throw new RuntimeException("请勿重复提交");
        }
        // 标记请求已经处理过
        redisTemplate.opsForValue().set(key, "1", idempotent.expire(), TimeUnit.SECONDS);
        // 处理请求
        return joinPoint.proceed(args);
    }

    /**
     * 获取redis key
     */
    private String getKey(ProceedingJoinPoint joinPoint) {
    
    
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = method.getName();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        Object[] args = joinPoint.getArgs();
        StringBuilder sb = new StringBuilder();
        sb.append(className).append(":").append(methodName);
        for (Object arg : args) {
    
    
            sb.append(":").append(arg.toString());
        }
        return sb.toString();
    }
}

2.3 Redis storage

We use Redis to store the processing status of requests. When a request is processed, we will store the processing status of the request in Redis and set an expiration time to ensure that the memory space of Redis will not be occupied all the time.

@Configuration
public class RedisConfig {
    
    

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
    
    
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

2.4 Sample code

Below is a sample code that demonstrates how to use @Idempotentannotations to achieve idempotence for interfaces.

@RestController
public class DemoController {
    
    

    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("/demo")
    @Idempotent(expire = 60)
    public String demo(@RequestParam("id") Long id) {
    
    
        // 处理请求
        return "success";
    }
}

3. Summary

This article introduces how to use SpringBoot custom annotations + AOP + redis to implement anti-interface idempotent repeated submission .

We first define a custom annotation @Idempotent, and then use AOP to determine whether a method is marked with this annotation. If this annotation is marked, special handling is required for this method to achieve idempotency. Finally, we use Redis to store the processing status of the request, and set an expiration time to ensure that the memory space of Redis will not be occupied all the time.


The above is the whole content of this article. For more technical dry goods, click on my homepage to continue to follow up~ If you have any technical questions, welcome to communicate and discuss with us

I wish you a happy day!

Guess you like

Origin blog.csdn.net/GUDUzhongliang/article/details/130627185