关于java:如何做一个防重设计

9次阅读

共计 1940 个字符,预计需要花费 5 分钟才能阅读完成。

前言

在业务设计中防重设计是一个关键点,以接口设计为例,防重就是避免接口被屡次调用而产生脏数据,比方领取订单呈现反复领取,所以说防重至关重要,在如何防重之前咱们首先看一下是如何呈现反复申请的。

何时呈现屡次调用

屡次调用接口的呈现有主观原因比方:人为的反复申请攻打,用户的误操作等;也有客观原因比方:为了健壮性进行超时重试;

  • 反复申请攻打

对于这种歹意攻打,其实曾经属于平安领域了,咱们能够通过黑名单 + 限流来解决,下一步再思考防重解决;

  • 用户的误操作

比方用户在界面点击提交按钮,因为手误呈现屡次点击提交,这种状况客户端能够做一些解决,缩小用户的误操作,比方提交完按钮变灰等;当然提供方的防重解决也是必不可少的;

  • 超时重试

接口调用方为了保证系统的健壮性,往往会做一些重试解决,比方各种 RPC 框架都曾经帮咱们内置了容错机制,提供方须要做好防重;

如何做好防重解决,其实外围就是保障多个雷同的申请只有一次被执行,或者说屡次调用和一次调用产生的成果是一样的,也就是咱们常常说的要保障幂等性;

何时须要防重

所有的操作说到底都是增删改查,其实咱们真正须要做防重解决的,更重要的是减少和批改;查问和删除自身执行一次和屡次,产生的成果是一样的,有人造幂等性,尽管说有人造幂等性,然而查问和删除自身也是要耗费资源的,如果能避免反复执行,也能节俭资源;
减少和批改是必须要做防重的,减少能够以下单为例,批改能够以更新库存为例,如果没有做好防重结果是十分重大的;上面具体看看都有哪些防重的措施;

如何防重

下面说到次要是针对减少和批改须要做好防重解决,当然针对减少和批改其实是有不同的防重措施,也有对立的形式,上面别离介绍;

对立防重

  • token 机制

服务器须要提供获取 token 机制的服务,这样每次客户端申请的时候先获取 token,服务器端会将 token 保留在 redis 中;客户端发送申请的时候会带上 token,这样服务器端能够拿到 token 间接去 redis 中做删除解决,依据返回值判断是否胜利:

localhost:0>get token
"111"
localhost:0>del token
"1"
localhost:0>del token
"0"

通过 redis 寄存 token,这样在分布式环境下也能很好的工作;

  • 防重 key

依据申请参数生成 md5 密文,而后用此密文作为 key 存入 redis 中,能够通过应用 setnx 命令来保障只有一个能保留胜利;

key = MD5.md5("param1="+param1+"&param2="+param2...)

localhost:0>setnx key 1
"1"
localhost:0>setnx key 1
"0"
  • 防重表

能够利用表的惟一索引束缚,能够应用相似防重 key 作为惟一索引字段,屡次申请过去只会有一个插入胜利,为了避免防重表数据过多,能够启动一个定时器定时清理;

以上几种形式其实和具体的业务关系不大,能够实用大部分场景;而且通过 redis 或者数据库,以及原子性操作来保障在分布式环境下也能够很好的运行;

插入防重

  • select+insert

插入数据最先想到就是先查看有没有,而后在插入,然而这样显著存在两个操作,不是原子性操作,单节点下还能通过锁来解决,分布式环境下就须要用到分布式锁来保障原子性了;当然也能够联合其余形式一起应用,比方上面的惟一主键机制;

  • 惟一主键

这种形式其实就是不间接应用数据库的自增主键了,应用分布式 id 算法生成,这样在插入数据的时候就能够通过惟一主键来进行束缚,保障只会有一条胜利;

  • 回滚机制

有些业务其实是有正向流程和逆向流程,比方领取订单,在接管到银行返回的领取胜利告诉时判断订单的状态,如果曾经领取胜利,能够间接走退款流程;

更新防重

  • 乐观锁形式

乐观锁常被用在更新场景中,如上面通过版本号的形式来实现:

update table_name
    sale = sale + 1,
    version = version + 1,
WHERE id = #{id}
AND version = #{version}
  • 状态机乐观锁

在相干订单的业务中,很多都会波及到状态机,状态是流转的,有了上一个状态才会有下一个状态,比方常见的购物订单包含:提交未领取,领取胜利,待发货,发货中,已签收等,每个状态都须要前置状态;

update table_name
   set  status = 下一个状态
   where  id = #{id} 
   and status = #{status} 

总结

防重的办法很多,咱们往往须要更加本人的业务做相干的抉择,不同的业务,不同的业务量,不同的容忍水平都会影响咱们如何去做防重;每种形式也不是都是独立存在的,有时候往往须要多种形式整合起来。

感激关注

能够关注微信公众号「 回滚吧代码 」,第一工夫浏览,文章继续更新;专一 Java 源码、架构、算法和面试。

正文完
 0