记录一次因执行时间过长锁已经释放导致finally块再次unlock引发的异常

一、前言

因为我的一个需求需要请求一个耗时比较长的接口(耗时长其实是对接方的锅),该接口交给了Spring事务管理,并且使用了分布式锁,但是在请求的时候,出现error,看日志发现是unlock的时候没有锁可以去解锁,才爆出的异常。然后又由于使用了事务,导致整个请求的数据被回滚。
在这里插入图片描述
然后就通过日志看请求时间和这次异常的时间,发现整整相差了1分钟,而分布式锁设置的时间只有5s

    @Override
	@PostMapping(path = "xxxxx")
	@Transactional(rollbackFor = Exception.class)
	public Result create(@RequestBody Request request) {
	// 通过缓存防止重复申请
		String lockDate = new StringBuilder().append(request.getId()).toString();
		DistributedLock lock = distributedLockService.getLock(LOCK_PREFIX + lockDate);
		if (!lock.tryLock(0, 5, TimeUnit.SECONDS)) {
			return Result.error(100, "请勿重复请求");
		}
		try{
		    //业务代码
		}catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			logger.warn(e.getMessage());
			return Result.error(100, "证书申请失败");
		} finally {
		     lock.unlock();
		}

二、解决

这就导致tryLock设置的时间已经超过,锁此时已自动释放,那么走到finally块时,又执行lock.unlock(),导致上述异常,而数据又回滚,就会产生好像执行了但实际上啥也没有的情景。
那么我们的解决思路有两种:

  • 第一种延长锁的持续时间,但是这个我之前说过这个超时是因为对接方的锅,如果使用其他对接方请求这个接口还是很快的。那么这个想法就先pass
  • 那第二种解决办法也是最后采取的解决方式,我们知道其实这个bug引起的最不好的体验就是整个请求产生的数据因为发生Bug,被Spring事务回滚了,导致我们想知道发生了啥,去db中找也找不到,而且更为重要的是,如果采用上一种延长锁的持续时间,可能会导致锁的竞争更加激烈,会影响系统的性能,并且其实大部分请求并不需要那么久,只是个别请求产生的延迟导致的Bug.那么最终采取的解决方式也比较简单:就是在finally块处,对lock.unlock进行try/catch捕获,如果捕获到异常,什么也不处理,这样就算有时候出现超时,超过了锁的持续时间,就算锁已被释放,这个时候去执行finally块被捕获,也不会被Spring事务回滚
@Override
	@PostMapping(path = "xxxxx")
	@Transactional(rollbackFor = Exception.class)
	public Result create(@RequestBody Request request) {
	// 通过缓存防止重复申请
		String lockDate = new StringBuilder().append(request.getId()).toString();
		DistributedLock lock = distributedLockService.getLock(LOCK_PREFIX + lockDate);
		if (!lock.tryLock(0, 5, TimeUnit.SECONDS)) {
			return Result.error(100, "请勿重复请求");
		}
		try{
		    //业务代码
		}catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			logger.warn(e.getMessage());
			return Result.error(100, "证书申请失败");
		} finally {
		   try{
		     lock.unlock();
		     }catch(Exception e){
		     }
		}

当然,唯一不好的,就是如果你的公司如果使用了sonarLint,会爆

Unlock this lock along all executions paths of this method

但是这个并不会产生什么大问题,不去管它就好,如果大家有什么更好的解决方案,可以评论区一起讨论。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨~旋律

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值