ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

java接口的幂等性及解决方案

2022-11-11 14:25:21  阅读:58  来源: 互联网

标签:java 函数 学习 系统 语言 平台 方法 安装  传递 引用


一、什么情况下需要幂等

用户多次点击按钮 用户页面回退再次提交 微服务相互调用,由于网络问题,导致请求失败,feign触发重试机制

二、幂等性解决方案

2.1 token机制(令牌)

在加载页面详情时候,服务器会顺便生成一个token一起返回给前端,服务端同时也在Redis中保存这个token数据,前端并不展示这个token,但当页面点击提交按钮时候,会在携带上这个token参数,此时后端便会先校验前端提交请求的token与redis中的token是否一致,一致的话即是第一次请求,执行业务代码并在Redis中删除该token,当用户还是携带上次的验证码多次提交,此时服务器判断redis中验证码不存在,便可能是多次提交的情况,不再执行业务代码,保证业务的幂等性,不被重复执行。 问题1:先删除token还是后删除token ·先删除可能导致, 删除token以后,服务器闪断,业务确实没有执行,此时重试还带上之前token,由于防重设计导致,请求还是没有走到执行业务代码那块逻辑。 ·后删除可能导致,业务处理成功,但是服务闪断,出现超时,没有删除token,别人继续重试,导致业务被执行两遍 解决:我们最好设计为先删除token,如果业务调用失败,就重新获取token再次请求。 问题2:如何保证token 获取、比较和删除必须是原子性 redis.get(token) 、token.equals、 redis.del(token)如果这3个操作不是原子,可能导致,高并发下,都get到同样的数据,判断都成功,导致业务并发执行 解决:可以在redis使用lua脚本完成这个操作

//  redis+lua脚本 原子验证令牌防止重复提交攻击
        String script = "if redis.call(get, KEYS[1]) == ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end";
        String orderToken = "现在的令牌";
        //  return 0 失败  1 成功
        Long result = stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),Arrays.asList("要验证的KEY"), orderToken);

2.2 各种锁机制

1)数据库悲观锁

//0.开始事务
begin;
//1.查询出商品信息
select status from t_goods where id=1 for update;
//2.根据商品信息生成订单
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status为2
update t_goods set status=2;
//4.提交事务
commit;

2)数据库乐观锁 适用读多写少的情况

update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};

3)业务层分布式锁 redison可以解决,代码实现

@Override
    @RedisLock(lockedPrefix = "model:replace:materialId:", timeOut = 5000)
    @Transactional(rollbackFor = Exception.class)
    public void changeSpuSync(@LockedObject Integer materialId, String userName) {
          
   
        log.info("模型表监听物料档案id={}是否修改了spu", materialId);
        ModelChangeSpuReq modelChangeSpuReq = new ModelChangeSpuReq(materialId, userName);
        this.dealChangeSpuSyncModel(modelChangeSpuReq);
    }

2.3 各种唯一约束

1)数据库的唯一约束: 利用主键的唯一性 2)redis set防重: 很多数据需要处理,只能被处理一次,比如我们可以计算数据的MD5将其放入redis的set,每次处理数据,先看这个MD5是否已经存在,存在就不处理。(百度上传文件秒传机制,如果该文件已经存在,就无需重复上传) 2.4 防重表 使用订单号orderNo做为去重表的唯一索引, 把唯一索引插入去重表, 再进行业务操作,且他们在同一个事中。这个保证了重复请求时,因为去重表有唯一约束,导致请求失败,避免了幂等问题。这里要注意的是,去重表和业务表应该在同一库中,这样就保证了在同一个事务,即使业务操作失败了,也会把去重表的数据回滚。这个很好的保证了数据一致性。

2.5 全局请求唯一id 前端每次调用接口请求时,生成一个唯一id,redis将数据保存到集合中(去重),存在即处理过。 全链路tranceId:可以使用Nginx设置每一个请求的唯一id;也可方便系统的链路追踪,该id并不能解决去重问题,当A系统调用B系统,B系统是否产生重试机制时候,可以根据这个id去判断

标签:java,函数,学习,系统,语言,平台,方法,安装,,传递,引用
来源:

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有