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

 更新时间:2021年11月08日 10:03:32   作者:yuec1998  
本文主要介绍了java接口的幂等性及解决方案,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

一、什么情况下需要幂等

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

二、幂等性解决方案

2.1 token机制(令牌)

在加载页面详情时候,服务器会顺便生成一个token一起返回给前端,服务端同时也在Redis中保存这个token数据,前端并不展示这个token,但当页面点击提交按钮时候,会在携带上这个token参数,此时后端便会先校验前端提交请求的token与redis中的token是否一致,一致的话即是第一次请求,执行业务代码并在Redis中删除该token,当用户还是携带上次的验证码多次提交,此时服务器判断redis中验证码不存在,便可能是多次提交的情况,不再执行业务代码,保证业务的幂等性,不被重复执行。

问题1:先删除token还是后删除token

先删除可能导致, 业务确实没有执行,重试还带上之前token,由于防重设计导致,请求还是不能执行。后删除可能导致,业务处理成功,但是服务闪断,出现超时,没有删除token,别人继续重试,导致业务被执行两遍
解决:我们最好设计为先删除token,如果业务调用失败,就重新获取token再次请求。

问题2:如何保证token 获取、比较和删除必须是原子性

redis.get(token) 、token.equals、 redis del(token)如果这两个操作不是原子,可能导致,高并发下,都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去判断

proxy_set header X-Request-ld $request_id;

总结

总之,一般情况下,几种方式的优选级使用顺序可以这样:分布式锁 > 乐观锁 > JVM锁 > 唯一约束 > 数据库悲观锁

当然,幂等性设计不能脱离业务实际来讨论,一定要根据实际业务场景选择合适的幂等性解决方案。

到此这篇关于浅谈java接口的幂等性及解决方案的文章就介绍到这了,更多相关java接口的幂等性内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java Swing JFrame框架类中setDefaultCloseOperation的参数含义与用法示例

    java Swing JFrame框架类中setDefaultCloseOperation的参数含义与用法示例

    这篇文章主要介绍了java Swing JFrame框架类中setDefaultCloseOperation的参数含义与用法,结合实例形式分析了Swing组件的JFrame框架类中setDefaultCloseOperation方法的简单使用技巧,需要的朋友可以参考下
    2017-11-11
  • Maven多模块之父子关系的创建

    Maven多模块之父子关系的创建

    这篇文章主要介绍了Maven多模块之父子关系的创建,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • Spring整合Mybatis的全过程

    Spring整合Mybatis的全过程

    这篇文章主要介绍了Spring整合Mybatis的全过程,包括spring配置文件书写映射器接口的实例代码,本文给大家介绍的非常详细,需要的朋友可以参考下
    2021-06-06
  • Java向数据库中插入数据后获取自增ID的常用方法

    Java向数据库中插入数据后获取自增ID的常用方法

    有时候因为新增的需求需要获取刚刚新增的数据的自增的主键ID,下面这篇文章主要给大家介绍了关于Java向数据库中插入数据后获取自增ID的常用方法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-11-11
  • LeetCode程序员面试题之递归乘法

    LeetCode程序员面试题之递归乘法

    在Java中,递归乘法是一种简单而有效的方法,可以用来计算两个数字的乘积。它的基本思想是:如果第一个数字是0,则乘积为0;如果第一个数字是1,则乘积为第二个数字;其他情况,则通过将第一个数字减1,并将第二个数字与自身相乘,来实现递归乘法。
    2023-02-02
  • 深入浅析Java中Static Class及静态内部类和非静态内部类的不同

    深入浅析Java中Static Class及静态内部类和非静态内部类的不同

    上次有朋友问我,java中的类可以是static吗?我给他肯定的回答是可以的,在java中我们可以有静态实例变量、静态方法、静态块。当然类也可以是静态的,下面小编整理了些关于java中的static class相关资料分享在脚本之家平台供大家参考
    2015-11-11
  • java动态添加外部jar包到classpath的实例详解

    java动态添加外部jar包到classpath的实例详解

    这篇文章主要介绍了java动态添加外部jar包到classpath的实例详解的相关资料,希望通过本文能帮助到大家,需要的朋友可以参考下
    2017-09-09
  • Spring Boot中RabbitMQ自动配置的介绍、原理和使用方法

    Spring Boot中RabbitMQ自动配置的介绍、原理和使用方法

    本文介绍了Spring Boot中RabbitMQ自动配置的介绍、原理和使用方法,通过本文的介绍,我们希望读者能够更好地理解Spring Boot中RabbitMQ的使用方法,并在项目中更加灵活地应用,感兴趣的朋友跟随小编一起看看吧
    2023-07-07
  • JavaApi实现更新删除及读取节点

    JavaApi实现更新删除及读取节点

    这篇文章主要介绍了JavaApi实现更新删除及读取节点,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • Java日常练习题,每天进步一点点(11)

    Java日常练习题,每天进步一点点(11)

    下面小编就为大家带来一篇Java基础的几道练习题(分享)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望可以帮到你
    2021-07-07

最新评论