分布式环境下,各个服务经常需要互相调用,如何保证调用的任务只被执行一次?本章基于数据的悲观锁,实现了任务的幂等性控制。
幂等性:多次执行所产生的影响均与一次执行的影响相同。比如在一次支付场景中,无论你点击多少次按钮,最终都应该只有一次扣款行为。
1. 根据全局业务流水号businessId查询数据库记录
IdempotentTask taskResult = idempotentTaskMapper.selectByBusinessId(businessId);
2. 判断之前是否有任务存在,如果不存在,本次任务落库。并发情况下也只会有一条写入成功
if (taskResult == null){ IdempotentTask task = new IdempotentTask(); task.setBusinessId(businessId); task.setCreated(new Date()); task.setModified(new Date()); //0表示初始化,1表示成功,-1表示失败 task.setStatus(0); try { int result = idempotentTaskMapper.insertTask(task); if (result > 0){ return true; } }catch (DuplicateKeyException e){ LOGGER.error("重复写入任务"); }catch (Exception e) { LOGGER.error("任务写入异常"); } return false; }
3. 如果上次任务已经存在,判断上次任务的状态。任务已经成功执行或者正在处理中,则拒绝本次任务。如果上次任务执行失败并且业务场景允许失败重试,那么更新任务状态为初始化,重新提交本次任务执行。
if (idempotentTask.getStatus() == 1){ LOGGER.warn("该任务已经成功执行过"); return false; }else if(idempotentTask.getStatus() == 0){ LOGGER.warn("该任务正在执行中"); return false; }else { //任务是失败状态 IdempotentTask task = new IdempotentTask(); task.setBusinessId(businessId); task.setCreated(new Date()); task.setModified(new Date()); //0表示初始化,1表示成功,-1表示失败 task.setStatus(0); try { int result = idempotentTaskMapper.updateTask(task); if (result > 0){ return true; } }catch (Exception e){ e.printStackTrace(); } return false; }
4. 本次任务会在业务的末端做成功或失败的终态更新。