redis + token机制实现接口幂等性校验

一、概念

幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次比如:

  • 订单接口, 不能多次创建订单
  • 支付接口, 重复支付同一笔订单只能扣一次钱
  • 支付宝回调接口, 可能会多次回调, 必须处理重复回调
  • 普通表单提交接口, 因为网络超时等原因多次点击提交, 只能成功一次等等

二、常见解决方案

  1. 唯一索引 – 防止新增脏数据
  2. token机制 – 防止页面重复提交
  3. 悲观锁 – 获取数据的时候加锁(锁表或锁行)
  4. 乐观锁 – 基于版本号version实现, 在更新数据那一刻校验数据
  5. 分布式锁 – redis(jedis、redisson)或zookeeper实现
  6. 状态机 – 状态变更, 更新数据时判断状态

三、本文实现

本文采用第2种方式实现, 即通过redis + token机制实现接口幂等性校验

四、实现思路

为需要保证幂等性的每一次请求创建一个唯一标识 token, 先获取 token, 并将此 token存入redis, 请求接口时, 将此 token放到header或者作为请求参数请求接口, 后端接口判断redis中是否存在此 token:

  • 如果存在, 正常处理业务逻辑, 并从redis中删除此 token, 那么, 如果是重复请求, 由于 token已被删除, 则不能通过校验, 返回 请勿重复操作提示
  • 如果不存在, 说明参数不合法或者是重复请求, 返回提示即可

五、项目简介

  • springboot
  • redis
  • @ApiIdempotent注解 + 拦截器对请求进行拦截
  • @ControllerAdvice全局异常处理
  • 压测工具: jmeter

说明:

  • 本文重点介绍幂等性核心实现,关于springboot如何集成redis、ServerResponse、ResponseCode等细枝末节不在本文讨论范围之内,有兴趣的小伙伴可以查看作者的Github项目:https://github.com/wangzaiplus/springboot/tree/wxw

六、代码实现

6.1、pom文件

<!-- Redis-Jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>

        <!--lombok 本文用到@Slf4j注解, 也可不引用, 自定义log即可-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.10</version>
        </dependency>

6.2、JedisUtil代码

package com.wangzaiplus.test.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

@Component
@Slf4j
public class JedisUtil {
   
   

    @Autowired
    private JedisPool jedisPool;

    private Jedis getJedis() {
   
   
        return jedisPool.getResource();
    }

    /**
     * 设值
     *
     * @param key
     * @param value
     * @return
     */
    public String set(String key, String value) {
   
   
        Jedis jedis = null;
        try {
   
   
            jedis = getJedis();
            return jedis.set(key, value);
        } catch (Exception e) {
   
   
            log.error("set key:{} value:{} error", key, value, e);
            return null;
        } finally {
   
   
            close(jedis);
        }
    }

    /**
     * 设值
     *
     * @param key
     * @param value
     * @param expireTime 过期时间, 单位: s
     * @return
     */
    public String set(String key, String value, int expireTime) {
   
   
        Jedis jedis = null;
        try {
   
   
            jedis = getJedis();
            return jedis.setex(key, expireTime, value);
        } catch (Exception e) {
   
   
            log.error("set key:{} value:{} expireTime:{} error", key, value, expireTime, e);
            return null;
        } finally {
   
   
            close(jedis);
        }
    }

    /**
     * 取值
     *
     * @param key
     * @return
     */
    public String get(String key) {
   
   
        Jedis jedis = null;
        try {
   
   
            jedis = getJedis();
            return jedis.get(key);
        } catch (Exception e) {
   
   
            log.error("get key:{} error", key, e);
            return null
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bst@微胖子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值