服务幂等设计

一、概念

 

幂等这个概念,是一个数学上的概念,即:f……(f(f(x))) = f(x)。用在计算机领域,指的是系统里的接口或方法对外的一种承诺,使用相同参数对同一资源重复调用某个接口或方法的结果与调用一次的结果相同。

二、业务场景

从业务场景上来说,如:现在互联网、电商的下单服务,同一个用户在短时间内调用某一个下单服务,只能下单成功一次;银行账户之间的转账,A账户给B账户转账,无论系统出现什么问题或bug,也只能转账成功一次;前端页面对相同表单的内容多次向后端发起提交请求,后端只能给出一个相同的结果等都属于幂等的范畴。分析这些业务场景后,我们发现,无论是下单服务、转账服务还是表单提交都是一个个业务请求,提供这些业务服务的接口或方法都应该保证无论服务是超时、重试或有bug等异常情况,都要满足业务上的处理结果正确。业务上的一次或多次请求,最终的处理结果是一致的,即:服务的幂等其实就是我们请求的幂等。

三、架构分析

从我们系统架构上进行分析,幂等该在哪一层做,怎么做呢?

上图为我们一个最常用的系统架构,前端发起一个请求到后端,我们的幂等该在哪一层来处理呢。我们不妨一层一层的分析,Nginx层需要做幂等吗,Nginx层的主要功能是做Web服务器、反向代理、负载均衡等,把请求转发到后端的服务器上,本身不参与具体的业务,所以在Nginx是不需要做幂等处理的。网关层,是负责权限校验、安全防御、认证鉴权、流量控制、协议转换、日志审计、监控等,本身也不含对任何业务的处理,所以其也不在幂等需要处理的范围内。业务逻辑层是对业务进行处理,编排,可能会改变数据,但对于数据的改变结果,最终也还是需要通过数据访问层,写入到数据库,所以业务逻辑层也不需要做数据幂等。数据访问层主要是和数据库交互,把业务逻辑层的结果写入数据库,对业务逻辑层提供读取、写入数据库的功能,在写入数据库的时候,针对每一次的写入,我们需要做幂等的考虑。而数据存储层,提供数据的存储,并不参与具体的业务逻辑计算。所以,通过对该架构的每一层的功能分析,我们得出对于请求的幂等处理,需要在数据访问层做处理,以便保证多次请求和一次请求的结果是一致的。

四、数据库操作

通过上面的分析,我们得出幂等需要在数据访问层来处理,再进一步分析,我们得出数据访问层的操作主要就是CRUD。下面逐一对每一种操作分析是否需要做幂等,以及怎么做。

R(read):对应的操作SQL语句为select。只要查询条件不变,在一定的时间内,执行一次和执行多次返回的结果肯定是相同的,所以其本身天然是幂等的,不需要我们再做处理。

         select * from user where id = 1;  天然幂等。

C(create):对应的操作SQL语句为insert。此时,需要分情况,如果用到的数据库主键为数据库自增,不考虑业务主键防重的情况下,每一次写入数据库就不是幂等的,所以为了保证幂等,需要在数据insert前做业务防重或是在数据库表上对业务主键加唯一索引。如果数据库主键不是自增,是由业务系统写入的,需要在业务系统里把数据库主键和业务主键做一对一映射,或是把数据库主键和业务主键做缓存,保证多次请求获取到的数据库主键和业务主键是一致的,确保写入数据库操作是幂等的。综合来说,就是相同的数据多次写入数据库后,能否保证只有一条数据。

insert into user values(id,age,sex,ts) values(1,18,‘male’,2019-03-10 10:22:23);

U(update):对应的操作SQL语句为update。更新操作时,一定是要用绝对值进行更新操作,而不要用相对值进行更新,相对值更新可能导致更新操作不幂等。

幂等:update user set age = 18 where id = 1;

非幂等:update user set age++ where id = 1;

        D(delete):对应的操作SQL语句为delete。删除操作时,如果删除的是一个范围,生成上应用要禁止该类操作,一般会把按范围操作删除转换为先按范围查询,再按查询的主键进行删除。而且按范围删除的操作不是幂等的。

       幂等:delete from user where id = 18;

非幂等:delete from user where id in select id from user order by id desc limit 10);该类操作线上应该要禁止。

五、常用手段

保证幂等的实现方式有多种,此处例举几类常用的方法,在实际应用中,根据业务场景进行选用。

1、前端页面提交时,页面token机制。进入页面时,从服务器获取token,在服务器端把token存入缓存,提交时把token带到服务器端进行验证;

2、乐观锁机制,使用数据库的版本号实现乐观锁,数据库更新时,判断版本号是否与查询时保持一致,一致更新成功,否则更新失败;

3、select+insert,数据写入前,先查询数据是否存在,存在直接返回,不存在则写入数据,保证写入数据库的数据正确性;常用于并发不高的一些后台系统或是防止任务的重复执行;

4、悲观锁机制,select * from table where id = '1234' for update;一般id为主键或唯一索引,仅锁定当前记录;

5、去重表,每一次写入或更新业务表时,先查询去重表是否已经存在记录,再操作业务表。

6、数据库唯一索引,为业务表建立唯一索引,避免业务数据多次写入;

7、状态机,业务状态的变更之间是有条件的,必须按设定的状态条件进行更新;

实现上述几类操作时,一般会用到的工具有:Redis、Zookeeper等。

在实际开发中,保证提供的接口或服务的幂等性,是一个最基本的技术要求,希望通过该总结,能对还未理解幂等的同学有所帮助。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值