幂等性接口设计
背景
软件开发者在许多接口的设计中会遇到幂等性的问题,这是今天本博客的主题。
幂等性,可以这么理解,一个接口如果是幂等的,那么就是说,一次调用和二次调用该接口,只要入参一样,出参也一样,其对系统的影响是相同的。比较经典的例子是转账。
想想一下,你在做一个转账软件,里面有一个转账功能,某个把一定数额的钱转给另一个人。这个接口有可能会像下面这样
boolean trans(Account account,int mount);
这个接口调用一次的话,A用户就会扣除mount数额的钱,B用户就会增加mount数额的钱。
请问,如果由于弱网原因,前端接连向服务端发送了2个转账请求,此时服务端如果没有处理好的话,A用户就会转2笔钱给B用户,这显然是不符合业务需求的。
那么该如何处理这个问题呢?答案就是设计一个幂等性的转账接口!!!
我当前的项目中有一个预定需求,该需求要求用户登录APP预定某一个场次,这个接口需要满足幂等性,也就是一个用户不能重复预定同一个场次。
一般的幂等性有2类要求:
时间性要求:是幂等几秒钟呢,还是几分钟,还是永远幂等
空间性要求:是对交易流水号幂等,还是对用户ID幂等
不同的要求,有不一样的解决方案、难度和成本,这需要具体分析业务的需求,采用适合的方案。
我这里给出2种设计方案:
-
第一种,对性能要求略低、访问量不大、对一致性要求较高的场景下,考虑使用DB数据约束的方案。 这是一种简单的幂等性方案,数据库中设计好唯一约束,当请求到达时根据幂等关键字构造幂等记录,该记录在数据库中保证了唯一性条件,当同一个请求第二次到达数据库时,
数据库会抛出一致性约束不满足的异常,捕获到该异常,就知道之前已经有记录在数据库里了。
这种方案的缺点就是性能不太好,优点是实现简单,只要数据库中设置一下约束、代码里捕获异常就好了。 -
第二种,对性能要求较高,访问量较大、一致性要求不那么严格的场景下,考虑使用REDIS+定时持久化的方案。
当请求到达时,在REDIS中构造幂等记录,并外挂一个程序定时将记录持久化到持久化设备中。如果业务要求若干小时内幂等,可以让外挂程序只持久化那些若干小时之前存在的记录即可
这里需要注意的一点是,如果在REDIS中找不到记录时需要到数据库中去找一次,还是找不到才算真的没有。 这种方案的缺点是实现复杂、且要求对REDIS进行高可用设计,因为业务的核心大部分由REDIS承载了,如果REDIS挂了,系统就不可用了,优点是性能比较高,因为引入了REDIS这种内存数据库。
最后我要说明的一点是,幂等性与你是不是分布式、是不是J2EE是没有关系的
一个幂等的操作典型的如:
把编号为5的记录的A字段设置为0,这种操作不管执行多少次都是幂等的
一个非幂等的操作典型的如:
把编号为5的记录的A字段增加1,这种操作显然就不是幂等的。