Anti-replay operation log record and submit

Due

Send a test shots seemed to say that because of the network card, leading to add a data point N times, results in a list has been added to the N.

analysis

View Code, before inserting data, there is a query whether the field contents based on data already exists, exists to update, or add. Still this happens, many cases can lead to:
1, when concurrent requests come in, concurrent queries are not present, the final concurrent insert, resulting in dirty data;
2, distributed applications, in the implementation of the relevant insert operation, no database operated lock table.
The second point is more complex, then back process, the first process point 1, the same user's operation (the same request) returns the operation result must wait before allowing a second request, thus avoiding complicated operation request.

demand

1, after sending the request until the front end, no response is returned, the operation disable off button
2, the rear end of the recording operation of the user log, add, modify, and delete operations submitted anti-replay

Design

Finding information on the Internet by recording the operation log and anti resubmitting commonly used AOP section for processing, and lock as identification by redis

process

1. Define a comment

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

    String value() default "";

    int lockTime() default 10;
}

2, a section increasing method

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Resource
    private AsyncTaskService asyncService;

    @Pointcut("@annotation(logAndNoRepeatSubmit)")
    public void pointcut(LogAndNoRepeatSubmit logAndNoRepeatSubmit) {
    }

    @Around(value = "pointcut(logAndNoRepeatSubmit)")
    public Object around(ProceedingJoinPoint point, LogAndNoRepeatSubmit logAndNoRepeatSubmit) throws Throwable {
        long beginTime = System.currentTimeMillis();
        int lockSeconds = logAndNoRepeatSubmit.lockTime();
        String key = LogAspectUtil.getKeyFromRequest();
        String clientId = UUID.randomUUID().toString();
        boolean isSuccess=false;
        //在并发情况下,redis请求会有大概10ms的请求时长,因此加锁,避免redis还没有执行加锁,其他请求已经进来而导致与预期不一致的情况
        synchronized (this) {
            isSuccess = RedisUtil.tryLock(CacheConst.IDEMPOTENCE + key, clientId, lockSeconds);
        }
        if (isSuccess) {
            Object result = null;
            try {
                result = point.proceed();
            } catch (Throwable e) {
                log.error("不重复提交日志", e);
                throw e;
            } finally {
                RedisUtil.del(CacheConst.IDEMPOTENCE + key);
            }
            long time = System.currentTimeMillis() - beginTime;
            Long uid = UserCacheUtil.getUserInfo().getId();
            String userName = UserCacheUtil.getUserInfo().getName();
            String ip = WebUtil.getIP();
            //记录操作日志
            OperationLogDTO dto = LogAspectUtil.handleLog(point, time, result, uid, userName, "COM-PC", ip);
            saveLog(dto);
            return result;
        } else {
            return R.fail("重复请求,请稍后再试");
        }
    }

    private void saveLog(OperationLogDTO dto) {
        // 保存系统日志,异步方法保存,或者放到MQ去执行
       asyncService.saveLog(dto);
    }
}

3, designed to operate logging content:

CREATE TABLE `operation_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `uid` bigint(20) DEFAULT NULL,
  `user_name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,
  `operation` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,
  `response_time` bigint(5) DEFAULT NULL,
  `method` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,
  `params` text CHARACTER SET utf8mb4,
  `ip` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,
  `ctime` datetime DEFAULT NULL,
  `status` bit(1) DEFAULT NULL,
  `response_code` int(4) DEFAULT NULL,
  `response_msg` varchar(1000) CHARACTER SET utf8mb4 DEFAULT NULL,
  `request_side` varchar(20) CHARACTER SET utf8mb4 DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2690 DEFAULT CHARSET=utf-8;

4, the test
using jmeter do concurrent 5 Test requests
can be seen that when the first request is not completed, all other requests are denied

to sum up

Anti-heavy submit avoid the appearance of dirty data to some extent, the subsequent need to improve the concurrent processing of distributed applications.

Released four original articles · won praise 0 · Views 36

Guess you like

Origin blog.csdn.net/m0_37453314/article/details/105409513