跳到主要内容

解决方案

一、token机制

场景一:前端重复提交

前端发起请求时,进行多次提交

解决方案:

token的获取可以和前端进行沟通,例如:在进入订单提交页面时(还未点击提交订单),通过钩子函数去获取token

image-20240612235456816

1.新建一个注解,并编写AOP切面类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Idempotent {
}
@Slf4j
@Component
@Aspect
public class IdempotentAspect {
@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Pointcut("@annotation(top.hyqstudio.idempotent.annotation.Idempotent)")
public void pointcut() {
}

@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 使用RequestContextHolder来获取当前线程的请求
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request= attributes.getRequest();
String token = request.getHeader("token");
log.info("token:" + token);
if (ObjectUtils.isEmpty(token)) {
log.error("token为空,非法请求");
throw new RuntimeException("token为空,非法请求");
}
Boolean b = redisTemplate.delete(token);
if (Boolean.FALSE.equals(b)) {
log.error("重复请求");
throw new RuntimeException("重复请求");
}
return joinPoint.proceed();
}
}

2.编写两个接口

一个获取token接口,一个业务接口,其中业务接口加上 @Idempotent注解

@RestController
@RequestMapping("/idempotent")
public class IdempotentController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;

@GetMapping("/token")
public String getToken() {
String token = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(token, token, 5, TimeUnit.MINUTES);
return token;
}

@Idempotent
@PostMapping
public String idempotent() {
//提交订单
//业务逻辑。。。。
return "success";
}
}

3.jmeter测试

提前获取token

18f260f2-c665-4d32-ba65-7d88579fd840

image-20240613000401777

配置jmeter参数

image-20240613000523455

image-20240613000545211

image-20240613000625587

结果为一个请求成功:

image-20240613000658566

二、分布式锁

三、MySQL去重表