栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

分布式接口幂等性的实现

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

分布式接口幂等性的实现

接口幂等性

幂等性:f(f(x)) = f(x),幂等元素运行多次,还等于它原来的运算结果。在系统中,一个接口运行多次,与运行一次的效果是一致的。

什么时候需要幂等性?

并不是所有的接口都要求幂等性,要根据业务设计。重复提交、接口重试、前端操作抖动等场景,例如用户一次提交一个订单,支付时只能扣一次钱。

幂等性策略

核心思想:通过**唯一的业务单号**保证幂等性。

非并发的情况,可以查询某个业务是否操作过,没有则执行(查询券是否使用过);并发时,操作过程加锁(分布式锁)。

select操作,不对数据有影响,天然幂等。

delete操作,第一次删除的话,不存在幂等性问题。

update操作

1、有唯一业务号的update操作

直接赋值是幂等的;set数据自增,不幂等。

策略是更新操作传入数据版本号,通过乐观锁实现幂等性。

2、没有唯一业务号,同下。

insert操作的幂等性

1、有唯一业务号的insert操作,例如:秒杀,商品ID+用户id

实现方式:

可通过分布式锁,保证接口幂等

业务执行完成后,不进行锁释放,让其自动过期释放

2、没有唯一业务号的insert操作,如用户注册,点击多次。

实现方式:没有唯一业务号,就要创建唯一业务号。

使用Token机制,保证幂等性

进入到注册页,后台统一生成Token,返回前台隐藏域中;用户提交,将token一同传入后台;使用token获取分布式锁,完成Insert操作

执行成功后,不释放锁,等待过期自动释放

3、混合操作幂等性

同样使用token机制。获取分布式锁,没有获取锁的就返回失败。

利用Token实现幂等性

假设用户提交订单前没有唯一业务id,此时可以考虑使用Token做幂等校验,创建一个唯一的业务id。

思路:用户进入订单提交页面,请求后端接口,根据sessionId保存一个Token到Redis中;用户提交订单时,携带Token,后端接口中校验Redis中Token与订单携带的Token是否一致,一致则允许订单操作,并删除Redis Token。

为了防止多次请求同时获取到同一个Redis Token,因此需要加分布式锁。

简要代码

@ApiOperation(value = "获取订单Token", notes = "获取订单Token", httpMethod = "POST")
@PostMapping("/getOrderToken")
public ServerResponse getOrderToken(HttpSession session) {
    String token = UUID.randomUUID().toString();
    redis.set("ORDER_TOKEN_" + session.getId(), token, 300);
    return ServerResponse.createBySuccess(token);
}

@ApiOperation(value = "用户提交订单", notes = "用户提交订单", httpMethod = "POST")
@PostMapping("/create")
public ServerResponse create(@RequestBody SubmitOrderBO submitOrderBO,
                             HttpServletRequest request,
                             HttpServletResponse response) {
    // 防止并发创建多个订单,加分布式锁
    String lockKey = "LOCK_KEY_" + request.getSession().getId();
    RLock lock = redissionClient.getLock(lockKey);
    lock.lock(5, TimeUnit.SECONDS);
    try {
        // 接口幂等性操作,校验Token
        String orderTokenKey = "ORDER_TOKEN_" + request.getSession().getId();
        String orderToken = redis.get(orderTokenKey);
        if (StringUtils.isBlank(orderToken)) {
            throw new RuntimeException("orderToken不存在");
        }
        if (!orderToken.equals(submitOrderBO.getToken())) {
            throw new RuntimeException("orderToken不正确");
        }
        // 请求正确后删除Token
        redis.del(orderTokenKey);
    } finally {
        lock.unlock(); // 也要try catch一下
    }
    
    // 其他订单校验、提交等操作。。。
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/867663.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号