一、防重复提交:
提交订单时,网速很慢,用户不知道,点击了多次提交订单,有可能导致数据库里面一个订单被插入了多次。
保证提交订单的幂等性。幂等性:提交一次和提交一百次都是一样的。
在分布式系统中,无论是给页面提交数据,表单的提交,还是分布式系统A服务和B服务相互调用,有可能一个方法执行多次,用户多次点击或者A服务一次调用B服务不成功,再次调用,无论怎么调用,都要保证接口的幂等性。
二、什么是幂等性?
接口幂等性就是用户对于同一操作发起的请求或者多次请求的结果是一致的。
场景:支付场景,用户购买了商品支付扣款成功,但是返回结果的时候网络异常,此时钱已经被扣掉了,用户再次点击按钮,此时会进行第二次扣款,但是,无论提交支付多少次 ,此过程却只能被成功执行一次即,需要保持幂等性
三、那些情况需要防止
- 用户多次点击按钮
- 用户页面回退再次提交
- 微服务互相调用,由于网络原因问题,导致请求失败,feign触发重试机制
四、解决方案
1、token机制
去12306买票,选定座位,点击提交,输入验证码,带上验证码,并且是对的,才能将选坐订单提交上去;如果是错的,或者是上一次的验证码,就不能提交订单———令牌机制。2、令牌机制工作原理:
提交订单,给页面服务端一个令牌(123456),点击提交订单按钮,发送请求(带上令牌),服务器已经存储了这个令牌,验证请求上带的令牌与服务器上的令牌是否一致,一致则创建订单,并删除服务器上订单。不论点击多少次提交订单按钮,带的令牌都是123456,所以第一次可以执行。其他都不会执行3、令牌机制的危险性:
服务端令牌和提交订单请求带的令牌,怎么验证令牌,才是比较完整的操作,不会出错。
提交订单后**①先删令牌,再执行业务;②还是**业务执行成功以后,再删除令牌?
如果是①先**删令牌的话,匹配上令牌,删除服务器令牌,提交订单,订单创建完成了,第二次再点击提交订单带之前的令牌就不能再匹配上服务器令牌了(被删除了)。
(1)controller请求过来了(带上token=123456)
redis.get(serverToken) //分布式系统下,一般存储在redis中
(2)if(serverToken==token){
del(token);
service(); //业务逻辑执行
}
此时出现问题了;分布式系统下,并发大,若此时两个请求同时到redis中获得serverToken,又同时对比成功(serverToken==token),同时执行业务逻辑,则可能创建两个订单(风险)
最终解决方案
保证从redis中获取令牌,与请求带来的token对比,以及删除令牌,三个操作整体的原子性—(lua脚本**)
如果是②**业务执行成功以后,再删除令牌,**匹配上令牌,执行业务中,此时第二个、第。。个又匹配上令牌了,此时生成了不止一个订单,再删除已经没有意义了。
//6. 防重令牌A
String token = UUID.randomUUID().toString().replace(“-“, “”);
stringRedisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberResponseVo.getId(), token, 30, TimeUnit.MINUTES);
