运维就要无所不能,无所不会
- 一、幂等概念
- 二、幂等使用场景
- 三、幂等实现方案
- 3.1 db去重表
- 3.2 MVCC(多版本并发控制)
- 3.3 状态机约束
- 四、Ansible 幂等实现源码剖析
幂等是什么及Ansible幂等实现源码解析
大家好,我是史丹利「Stanley」,昨天被老板「粉丝」批评说近段时间文章太软。。。今天我们硬一把,来聊聊幂等。先上图
Ansible-Copy-1st
Ansible-Copy-2st
Notice: 第一次执行结果和第二次一致,所以第二次执行命令时并没有实质变更,即不对目标对象做变更。
一、幂等概念
幂等(idempotence)一词原为数学上的概念,用一个最直观的数学式子表达为:
f(f(x)) = f(x)
对应到软件开发领域,即为同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的,实际上就是接口的可重复调用
(包括时间和空间上两个维度)。
不是要求返回值完全相同,而且是指后续多余的调用对系统的数据一致性不造成破坏。
- 写操作
如果第一次写入是成功的,后续的写入应该抛出异常或者空操作,或者执行了写入但是未对数据造成变化。
作业:数据库如何保证写操作幂等?
- 读操作
读取类操作,需要保证其实现上是真正的读取,不能在读操作中夹带写操作。如HTTP get method
好的如:秒杀按钮只能点击一次,之后会变成灰色无法点击。如微信红包
差的如:某东秒杀按钮可重复点击,每次返回“茅台已被抢完”...
二、幂等使用场景
- 应用场景类
- 某东抢茅台
- 某信抢红包
- 技术场景类
- ansible执行命令,如符合幂等刚不会重复执行
- curl -X GET HTTPURL
三、幂等实现方案
方式不一,具体和业务场景有关联性。
3.1 db去重表
订单支付信息录入至去重表,每次支付请求先录入订单信息再执行支付行为。
如插入成功,且支付,如插入失败。则抛异常
图1-1:支付场景时序图
3.2 MVCC(多版本并发控制)
数据库并发请求场景下,根据唯一字段判断最新状态,唯一字段可以是版本号、JS生成的唯一码、表唯一索引等。如果唯一字段不存在,则无法获取数据或执行增、删、改等变更操作
代码语言:javascript复制update table_name set deposit = deposit-#{payment}, version = version 1 where orderId = #{orderId} and version = #{version}
3.3 状态机约束
通过程序代码逻辑等技术手段实现,在代码段是增设状态判断,如果状态机已处于下一个状态,这时候不能往回跳转到上一个状态,通过状态机的跳转约束,可以做到有线状态机的跳转约束,比如基于状态机实现的乐观锁:
代码语言:javascript复制update table set status=next_status where id=#{id} and status=#{status}
当前状态第一次被修改后,状态被修改为下一种状态,同一记录针对当前状态的其他修改会失败,程序跑出异常,这常见于并发场景的修改。
如下 Ansible
即通过该方式实现。
四、Ansible 幂等实现源码剖析
以文首 copy
模块为例。
# 文件所在位置
ansible/modules/files/copy.py
ansible-copy模块逻辑
- 幂等核心源码如下「请留意注释」
def main():
...
# 调用sha1先做md5校验
checksum_src = module.sha1(src)
checksum_dest = None
# Backwards compat only. This will be None in FIPS mode
try:
md5sum_src = module.md5(src)
except ValueError:
md5sum_src = None
# 设置状态机 changed
changed = False
...
if checksum_src != checksum_dest or os.path.islink(b_dest):
# 开始文件copy
....
# 成功copy
changed = True
else:
# 如果文件已经存在,且md5一样,则不执行操作。
changed = False
Notice: Ansible 的幂等通过各模块或插件,
AdHoc
和Playbook
只实现了命令分发执行和结果收集展示参考: 幂等实现[1]
~ over ~ 你学废了吗?
参考资料
[1]
幂等实现: https://segmentfault.com/a/1190000015884659