由于公司的老项目使用的是struts2+mybatis+spring,有秒杀的场景,但是每次秒杀都有失败的情况。然后我这边引入了redisson分布式锁,但是还是没有控制住库存。查看了下日志发现原来分布式锁生效了,但是并发下每个线程读取的库存信息都是一样的,我开始怀疑是代码层面没啥问题,数据库事务没有提交,新的线程读取到了脏数据。
最后觉得应该在数据库层面进行控制,引入了乐观锁。为了不引入新的字段使用修改日期字段作为版本控制。
- 第一步自定义注解
/**
* 重试注解
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface IsTryAgain {
}
- 第二步自定义切面
@Aspect
@Configuration
public class TryAgainAspect implements Ordered {
/**
* 默认重试几次
*/
private static final int DEFAULT_MAX_RETRIES = 3;
private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
public int getOrder() {
return this.order;
}
@Pointcut("@annotation(IsTryAgain)")
public void retryOnOptFailure() {
// pointcut mark
}
@Around("retryOnOptFailure()")
@Transactional(rollbackFor = Exception.class)
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
int numAttempts = 0;
do {
numAttempts++;
try {
//再次执行业务代码
return pjp.proceed();
} catch (TryAgainException ex) {
if (numAttempts > maxRetries) {
//如果大于 默认的重试机制 次数,我们这回就真正的抛出去了
throw new ApiException(ApiResultEnum.ERROR_TRY_AGAIN_FAILED);
}else{
//如果 没达到最大的重试次数,将再次执行
System.out.println("=====正在重试====="+numAttempts+"次");
}
}
} while (numAttempts <= this.maxRetries);
return null;
}
}
- 第三步在使用的地方添加注解
hibernate和mybatis-plus都集成了乐观锁的实现。hibernate中使用version和timestamp标签支持版本控制,mybatis-plus中使用OptimisticLockerInterceptor插件。
网友评论