幂等性为题
当我们在调用提交订单时 如果因为网络波动问题造成点击多次
调用方法或者接口不会改变业务状态 可以保证重复调用的结果和单次调用的结果一致
场景
1前端重复提交
2接口超时重试
3消息队列重复消费
天然幂等性行为
以sql语句为例
select * from user where id=1 无论怎么查询 结果都为1 不变
update user set age=18 where id=2
delete from user where id=1
insert into (user_id,username)values (1,'lcc') user_id 为主键自增
不具备幂等性
update user set age=age+1 where id=1
insert into(usr_id,username) value(1,'lcc') user_id不是主键 可以重复
解决方案
1 token机制 第一次访问服务器时 服务器会响应浏览器一个token信息(并由该服务器将token 存储在--?redis中) 该浏览器携带token去访问另一个服务时携带该token 该服务会去reids中验证该token是否和redis中的token一致 若相同
则删除该token
若出现重复提交的话 会在redis中获取不到该token信息 解决的表单的重复提交

上述步骤1234 必须保证原子性
否则 当并发访问时 可能会在redis删除token之前
其他线程获取到token
基于token解决幂等性问题 解决防重提交

订单防重校验 通过lock加锁的方式来实现原子性操作
try { lock.lock();//加锁 String redisToken = redisTemplate.opsForValue().get(key); if(redisToken!=null&& redisToken.equals(vo.getOrderToken())){ //表示第一次提交 //需要删除token redisTemplate.delete(redisToken); }else { //表示重复提交 return responseVO; } } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock();//释放锁 }
我们还可以使用redis脚本来实现原子性处理
ublic OrderResponseVO submitOrder(OrderSubmit vo) { //表示需要返回的响应对象 OrderResponseVO responseVO = new OrderResponseVO(); //获取当前登陆的用户信息 MemberVO memberVO = AuthInterceptor.threadLocal.get(); //1 验证是否重复提交 保证redis中的token的查询和删除时一个原子性操作 String key = OrderConstant.ORDER_TOKEN_PREFIX + ":" + memberVO.getId(); //根据lua脚本 String script="if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0"; Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(key), vo.getOrderToken()); if(result==0){ //表示验证失败 说明是重复提交 return responseVO; } //是第一次提交 令牌验证成功 开始下订单操作

浙公网安备 33010602011771号