1、背景:
幂等性操作就是某个操作重复执行多次对系统产生的影响只有一次,简单来说,重复操作在整体系统上来讲相当于只执行了一次。
业务开发中,有些业务操作是需要防止重复执行的,由于之前公司是做封闭区域自动驾驶业务的,以港口作业业务为例,像是一些通过前端对车辆进行控制的操作,例如任务下发、路径规划等操作,即使通过前端按钮疯狂点击或者出现并发情况,那么系统应当只正常执行一次对应逻辑。以及还有通过MQ接收第三方作业任务信息的操作,为了防止重复消费到同一个任务,也应该去做幂等性保护,等等...
针对于常规的增删改查来说,查询和删除是天然幂等的,查询不多说,删除,一般我们会根据主键ID进行删除,删除以后数据没有了自然不会再产生什么影响。新增,如果多次新增操作都在数据库添加一条重复记录,那么显然是有问题的。修改操作比较特殊,如果要修改的字段不是加减操作,只是设置一个固定的值,那么可以说是幂等的。
2、幂等性设计思路:
2.1、基于数据库:
例如,MySQL,可以通过添加唯一索引防止重复新增,选择一个或者多个合适的字段,一般是业务ID之类的,作为唯一索引,重复插入时,会插入失败。更新操作的话,可以选择使用一个版本号字段,在修改时带上这个版本号,只有当版本号一致时才修改要更新的字段。
2.2、基于业务规则:
例如,我在通过MQ进行任务消费时,会根据任务ID以及一些其它重要字段一起构成一个判定重复的规则,在执行业务逻辑前,先进行重复判断。所以,一般选择业务唯一的Key或者能够确保唯一的规则,通过缓存的方式,与之后的进行比对,重复则抛弃。
2.3、基于锁:
由于项目是做了集群化处理的,那么例如并发的执行了任务下发操作,JVM级别的锁显然不能用(单体项目可以),那么选择分布式锁进行处理,目的是为了确保任务下发逻辑只被执行一次。伪代码如下:
// 判断车辆是否空闲
if (state == IDLE) {
redis.lock();
try {
// 再次判断车辆是否空闲,防止并发环境下,多个线程都通过了第一次空闲检测
if (state == IDLE) {
// 执行任务下发逻辑
// 修改状态
}
}finally {
reids.unlock();
}
}
3、总结:
幂等性设计可以保障业务不被重复执行,在一些不允许重复执行的操作或者不允许并发导致重复执行的业务场景下,幂等性设计是一个非常好用的手段,以上是工作中常用的幂等性设计方式,不过,一般通过数据库去做不太好,要尽量避免无效操作数据库,所以经常通过其它两种方式去做,另外防止页面上疯狂重复点击操作的出现,也可以从前端做一些设置,例如,点击完就置灰等。