导读
本文次要从研发人员的角度,联合研发人员日常常见的各类业务场景,从经典零碎框架的每一层动手剖析幂等解决的机会。心愿通过这篇文章的剖析,让开发者在日常开发中对幂等的解决不再生疏。抓住导致申请、接口不幂等的实质,在工作中防止再陷入这个陷阱中。
幂等、幂等性这词,作为一个研发人员是再相熟不过的,那是否有深刻思考过幂等产生的背景、为什么须要幂等,如何做才是幂等的?明天将联合业务场景及申请的过程来剖析解决幂等(性)的办法。
1 概念
幂等这个概念,是一个数学上的概念,即:f……(f(f(x))) = f(x)。用在计算机领域,指的是零碎里的接口或办法对外的一种承诺,应用雷同参数对同一资源重复调用某个接口或办法的后果与调用一次的后果雷同。
2 业务场景
从业务场景上来说,如:当初互联网电商的下单服务,同一个用户在短时间内调用某一个下单服务,只能下单胜利一次;银行账户之间的转账,A 账户给 B 账户转账,无论零碎呈现什么问题或故障,也只能转账胜利一次; 前端页面对雷同表单的内容屡次向后端发动提交申请,后端只能给出一个雷同的后果等都属于幂等的领域。
试想一下,如果提供的这些服务不是幂等的,客户在下单时因为网络不稳固或是间断点了几次下单按钮,理论客户只下了一单,后果零碎里给客户生成了多单,那平台 / 商家将是无奈接受的,如果被“羊毛党”盯上,损失是无可估计的;银行之间的转账,A 账户原本理论给 B 账户只转了一百万,后果 B 账户收到了几百万,这在业务上是不可承受的。剖析这些业务场景,开发者发现,无论是下单服务、转账服务还是表单提交都是一个个业务申请,提供这些业务服务的接口或办法都应该保障无论服务是超时、重试或有故障等异常情况,都要满足业务上的处理结果是正确的。 业务上的一次或屡次申请,最终的处理结果是统一的,即:在肯定工夫内,服务的幂等其实就是申请的幂等。
3 架构剖析
从零碎架构上进行剖析,幂等该在哪一层去做,怎么做?
图 1 经典零碎框架图
上图为一个最常见的经典零碎框架图,Web 端发动一个申请到后端,幂等该在哪一层来解决呢?无妨一层一层地剖析。
Nginx 是否须要做幂等,Nginx 的次要性能是做 Web 服务器、反向代理、负载平衡等,把申请转发到后端的服务器上,自身不参加具体的业务,所以 Nginx 是不须要做幂等解决的;Gateway 是负责权限校验、平安进攻、认证鉴权、流量管制、协定转换、日志审计、监控等,自身也不含对任何业务的解决,所以其也不须要做幂等解决;Service 层通常是对业务逻辑进行解决、编排,可能会扭转数据,但对于数据的扭转后果,最终也还是须要通过数据拜访层,写入到数据库,所以 Service 层也不须要做数据幂等;DAO 层次要是和数据库交互,把 Service 层的后果写入数据库,对 Service 层提供读取、写入数据库的性能。
在写入数据库的时候,针对每一次的写入,可能返回不同的后果,此时就须要按场景进行具体的剖析看待;DataBase 层,次要提供数据的存储,并不参加具体的业务逻辑计算。所以,通过对该架构的每一层的功能分析,得出对于申请的幂等解决,须要在 DAO 层做解决,以便保障屡次申请和一次申请的后果是统一的。
4 数据库操作剖析
通过下面的剖析,得出幂等须要在 DAO 层来解决,再进一步剖析,得出 DAO 层的操作次要就是 CRUD。上面逐个对每一种操作剖析是否须要做幂等,以及怎么做。
R(read):对应的操作 SQL 语句为 select。只有查问条件不变,在肯定的工夫内,执行一次和执行屡次返回的后果必定是雷同的,所以其自身是幂等的,不须要再做解决。
select * from user where id = 1;
查问一次或屡次后果是统一的,所以是幂等的。
C(create):对应的操作 SQL 语句为 insert。此时,须要分状况,如果用到的数据库主键为数据库自增,不思考业务主键防重的状况下,每一次写入数据库就不是幂等的,所以为了保障幂等,须要在数据 insert 前做业务防重或是在数据库表上对业务主键加惟一索引。如果数据库主键不是自增,是由业务零碎写入的,须要在业务零碎里把数据库主键和业务主键做一对一映射,或是由独立服务提供数据库主键和业务主键的映射关系,保障屡次申请获取到的数据库主键和业务主键是统一的,确保写入数据库操作是幂等的。综合来说,就是雷同的数据屡次写入数据库后,是否保障只有一条数据。
insert into user (id,age,sex,ts) values(1,10,‘male’,2021-07-20 10:22:23);
U(update):对应的操作 SQL 语句为 update。更新操作时,肯定是要用绝对值进行更新操作,而不要用相对值进行更新,相对值更新可能导致更新操作不幂等。
幂等:update user set age = 10 where id = 1;
非幂等:update user set age++ where id = 1;
D(delete):对应的操作 SQL 语句为 delete。删除操作时,如果删除的是一个范畴,生产上最好是禁止该类操作;比拟举荐的做法是把按范畴操作删除转换为先按范畴查问,再按查问的主键进行删除。而且按范畴删除的操作不是幂等的。
幂等:delete from user where id = 1;
非幂等:该类操作要禁止。
delete from user where id in(select id from user order by id desc limit 10);
5 常见业务场景
保障幂等的实现形式有多种,此处例举几类常见的业务场景,在理论利用中,依据业务场景进行选用。
图 2 页面 token 机制解决流程
- 前端页面提交时,页面 token 机制。进入页面时,从服务器获取 token,在服务器端把 token 进行存储,提交时把 token 带到服务器端进行验证;常见的解决流程如下:
- 乐观锁机制,应用数据库的版本号实现乐观锁,数据库更新时,判断版本号是否与查问时保持一致,统一更新胜利,否则更新失败;
- select+insert,数据写入前,先查问数据是否存在,存在间接返回,不存在则写入数据,保障写入数据库的数据正确性;罕用于并发不高的一些后盾零碎或是避免工作的反复执行;
- 乐观锁机制,个别 id 为主键或惟一索引,仅锁定以后记录;
- select * from table where id = ‘1234’ for update;
- 去重表,每一次写入或更新业务表时,先查问去重表是否曾经存在记录,再操作业务表。
- 数据库惟一索引,为业务表建设惟一索引,防止业务数据屡次写入;
- 状态机,业务状态在变更之前是有条件的,必须按设定的状态条件进行更新;
在理论开发中,保障提供的接口或服务的幂等(性),是一个最根本的技术要求,心愿通过该剖析,能对还未了解幂等(性)的研发人员有所帮忙。