fzy-blog

java幂等性的控制

2019-05-24

我们来谈下高并发和分布式中的幂等处理
https://juejin.im/post/5c05f233e51d4524860fc51a

本文是从技术论坛上大家一人一句没有条理的讲解,我整理一下发到 CSDN 上,希望对大家有用。

什么是幂等性
抄用一段数学上的定义:f(f(x)) = f(x)。x 被函数 f 作用一次和作用无限次的结果是一样的。幂等性应用在软件系统中,我把它简单定义为:某个函数或者某个接口使用相同参数调用一次或者无限次,其造成的后果是一样的,在实际应用中一般针对于接口进行幂等性设计。举个栗子,在系统中,调用方 A 调用系统 B 的接口进行用户的扣费操作时,由于网络不稳定,A 重试了 N 次该请求,那么不管 B 是否接收到多少次请求,都应该保证只会扣除该用户一次费用。

加深对幂等性的了解
幂等性一般应用于协议设计,TCP 协议支持幂等吗?答案是肯定的,在网络不稳定时,操作系统可以肆无忌惮的重发 TCP 报文片段。TCP 协议能够保证幂等的核心在于 sequence number 字段,一个序列号的在较长的一段时间内均不会出现重复。对于应用层的协议设计,原理和 TCP 是类似的,我们需要一个不重复的序列号。再简单一点说,在一个业务流程的处理中,我们需要一个不重复的业务流水号,以保证幂等性。
举个实际应用场景:用户 A 在网页上发起一笔游戏充值请求,浏览器引导用户去银行支付,支付成功后系统给用户进行充值。
协议设计上,我们通过全局唯一的充值订单号贯穿整个业务流程,使该业务支持幂等。
我们先从一个简单的程序理解一下幂等性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

public class Main {
private int i = 0;

//这个方法不具有幂等性,每调用一次,它就会改变 Main 的状态(即改变了 i)
public void idempotent() {
i++;
}

//幂等性,无论这个方法调用多少次,它都不会改变 Main 类的状态。
public void simple() {
System.out.println(i);
}
}

看完这些,你似乎对幂等性有了更深的了解。那么幂等性问题会出现在哪些场景呢?
电商,第三方支付,抢红包等场景。
这些应用场景,你似乎看到了他们的共同特征。对,那就是高并发。
幂等控制的实现
HTTP 的幂等性
幂等表示:请求服务器一次或是多次,返回的结果均是一样的【select 】一般是 GET 请求

非幂等表示:请求服务器不同的次数,返回的结果将是不一样的[update delete] 一般是 POST 请求

HTTP 协议本身是一种面向资源的应用层协议,但对 HTTP 协议的使用实际上存在着两种不同的方式:一种是 restful,它把 HTTP 当成应用层协议,另一种是 SOA,它并没有完全把 HTTP 当成应用层协议,而是把 HTTP 协议作为了传输层协议,然后在 HTTP 之上建立了自己的应用层协议。

restful 风格,想了解的可以去看看 webservice 编程,这里不是本文的主题。

本文所讨论的 HTTP 幂等性主要针对 RESTful 风格的,不过正如上一节所看到的那样,幂等性并不属于特定的协议,它是分布式系统的一种特性;所以,不论是 SOA 还是 RESTful 的 Web API 设计都应该考虑幂等性。

重要方法 安全 幂等
GET 是 是
POST 否 否
PUT 否 是
DELETE 否 是
数据库幂等
数据库上的幂等和事务是一体的。

  1. 查询操作
    查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select 是天然的幂等操作
  2. 删除操作
    删除操作也是幂等的,删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回 0,删除的数据多条,返回结果多个) 3.唯一索引,防止新增脏数据
    比如:支付宝的资金账户,支付宝也有用户账户,每个用户只能有一个资金账户,怎么防止给用户创建资金账户多个,那么给资金账户表中的用户 ID 加唯一索引,所以一个用户新增成功一个资金账户记录 4.悲观锁
    获取数据的时候加锁获取
    select * from table_xxx where id=’xxx’ for update;
    注意:id 字段一定是主键或者唯一索引,不然是锁表,会死人的
    悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用
  3. 乐观锁
    乐观锁只是在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高。
    客户端幂等控制机制-token
    业务要求:
    页面的数据只能被点击提交一次
    发生原因:
    由于重复点击或者网络重发,或者 nginx 重发等情况会导致数据被重复提交
    解决办法:
    集群环境:采用 token 加 redis(redis 单线程的,处理需要排队)
    单 JVM 环境:采用 token 加 redis 或 token 加 jvm 内存
    处理流程:
  4. 数据提交前要向服务的申请 token,token 放到 redis 或 jvm 内存,token 有效时间
  5. 提交后后台校验 token,同时删除 token,生成新的 token 返回
    token 特点:
    要申请,一次有效性,可以限流

后期整理电商案例,直接上图来说一下一个电商网站在幂等控制上的方法。(待续。。。)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/loveblog1314/article/details/72649809

使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章