幂等性: f(f(x)) = f(x) 幂等元素运行多次,还等于它原来的运算结果 在系统中,一个接口运行多次,与运行一次的效果是一致的
根据唯一业务号去删除(也是天然幂等性)
第一次删除时,已将数据删除第二次再次执行时,由于找不到记录,所以返回的结果是0,对业务数据没有影响。可在删除前进行数据的查询。删除操作没有唯一业务号, 则要看具体的业务需求
例如:删除所有审核未通过的商品。 执行第二次删除操作,新的未审核通过的商品要不要删除? 根据业务需求而定根据唯一业务号去更新数据的情况
用户查询出要修改的数据,系统将数据返回页面,将数据版本号放入隐藏域用户修改数据,点击提交,将版本号- -同提交给后台后台使用版本号作为更新条件update set version =version+ 1 ,XXx= ${xxx} where id=xXxand version = ${version}更新操作没有唯一业务号,可使用Token机制
有唯一业务号的Insert操作,例如:秒杀,商品ID+ 用户ID
可通过分布式锁,保证接口幂等业务执行完成后,不进行锁释放,让其过期自动释放下面的注册以用户的名字作为唯一业务号 @Configuration public class ZkConfig { @Bean(initMethod="start",destroyMethod = "close") public CuratorFramework getCuratorFramework() { RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181", retryPolicy); return client; } } @Autowired private CuratorFramework zkClient; public int insertUser(User user) throws Exception { InterProcessMutex lock = new InterProcessMutex(zkClient, "/"+user.getUsername()); boolean isLock = lock.acquire(30, TimeUnit.SECONDS); if (isLock){ return userMapper.insertSelective(user); } return 0;
没有唯一业务号的Insert操作,比如:收货地址
使用Token机制,保证幂等性进入到注册页时,后台统一生成Token,返回前台隐藏域中使用Token获取分布式锁,完成Insert操作执行成功后,不释放锁,等待过期自动释放 public int insertUser(User user, String token) throws Exception { InterProcessMutex lock = new InterProcessMutex(zkClient, "/"+token); boolean isLock = lock.acquire(30, TimeUnit.SECONDS); if (isLock){ return userMapper.insertSelective(user); } return 0; }
1.生成Token返回给前端
@ApiOperation(value = "获取订单token", notes = "获取订单token", httpMethod = "POST") @PostMapping("/getOrderToken") public IMOOCJSONResult getOrderToken(HttpSession session){ String token = UUID.randomUUID().toString(); redisOperator.set("Order TOKEN"+session.getId(),token,30); return IMOOCJSONResult.ok(token); }2.订单的BO中加入redis属性
3.用户下单:注意两点①第一个次用户进入订单逻辑后,删除redis,②加入分布式锁防止多个用户获得token
public IMOOCJSONResult create(@RequestBody SubmitOrderBO submitOrderBO, HttpServletRequest request, HttpServletResponse response) { String orderTokenKey = "ORDER_TOKEN" + request.getSession().getId(); String lockKey = "LOCK_KEY"+request.getSession().getId(); RLock lock = redissonClient.getLock(lockKey); lock.lock(5, TimeUnit.SECONDS); try { String orderToken = redisOperator.get(orderTokenKey); if (StringUtils.isBlank(orderToken)) { throw new RuntimeException("orderToken不存在"); } boolean equals = orderToken.equals(submitOrderBO.getToken()); if (!equals) { throw new RuntimeException("orderTokenwrong"); } redisOperator.del(orderToken); }finally { lock.unlock(); }4.前端加载订单页面时,添加获取后端提取后端token的方法