接口幂等性

  • A+
所属分类:软件开发

1.幂等性

幂等性是分布式环境下常见的问题;幂等性指的是多次操作,结果是一致的。(多次操作数据库数据是一致的。)
常见的解决幂等性的方式有以下:
1.唯一索引;保证插入的数据只有一条;
2.token机制;每次接口请求前先获取一个token,然后再下次请求的时候在请求的header体中加上这个token,后台进行验证,如果验证通过删除token,下次请求再次判断token
3.悲观锁或者乐观锁,悲观锁可以保证每次for update的时候其他sql无法update数据(在数据库引擎是innodb的时候,select的条件必须是唯一索引,防止锁全表)【分布锁思路】
4.先查询后判断,首先通过查询数据库是否存在数据,如果存在证明已经请求过了,直接拒绝该请求,如果没有存在,就证明是第一次进来,直接放行。

2.redis实现幂等性

image.png

2.1 搭建redis,编写操作redis的API

这一步没啥好说的,网上一大堆。一般你出来工作了,也不会让你搭建。项目都已经搭好了,用就完事了。

2.2 token服务

public interface TokenService {

    /**
     * 生成token
     * @return 返回生成的token
     */
    String createToken();

    /**
     * 校验token
     * @param httpServletRequest http请求
     * @return 校验token结果
     * @throws Exception 抛出异常
     */
    boolean checkToken(HttpServletRequest httpServletRequest) throws Exception;}

import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import java.util.UUID;@Servicepublic class TokenServiceImpl implements TokenService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private static final String TOKEN_PREFIX = "token_prefix";

    private static final String TOKEN_NAME = "check_token";

    @Override
    public String createToken() {
        String str = UUID.randomUUID().toString();
        StringBuilder token = new StringBuilder();
        try {
            token.append(TOKEN_PREFIX).append(str);
            stringRedisTemplate.opsForValue().set(token.toString(), token.toString(), 10000L);
            boolean notEmpty = StringUtils.isNotBlank(token.toString());
            if (notEmpty) {
                return token.toString();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    @Override
    public boolean checkToken(HttpServletRequest request) throws Exception {
        String token = request.getHeader(TOKEN_NAME);
        if (StringUtils.isBlank(token)) {
            token = request.getParameter(TOKEN_NAME);
            if (StringUtils.isBlank(token)) {
                throw new OperationException("token不存在");
            }
        }
        String isExist = stringRedisTemplate.opsForValue().get(token);
        if (StringUtils.isBlank(isExist)){
            throw new OperationException("token不存在");
        }
        Boolean remove = stringRedisTemplate.delete(token);
        if (!remove) {
            throw new OperationException("token不存在");
        }
        return true;
    }}

主要的代码如上;这是对于Web环境的;如果是服务之间的调用。比如dubbo,fegin接口之间的调用。可以将Token作为参数传给服务端进行校验。总体来说,思路还是比较简单的。

作者:Coder_lan
链接:https://www.jianshu.com/p/b35c7e33ea27
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: