携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
前言
我们在开发的过程中会有遇到一些场景:
1.比如我们前端在提交数据的时候不小心点了两次,导致后端请求接口调用两次,会在我们数据库插入两条数据,虽然说ID不一样,但是其他字段还是一样的,这样我们在查询的时候就回查询到很多重复的数据。
2.还有就是我们在后端在请求接口超时的情况下,会去用重试机制去请求接口,但是请求的结果没能及时返回(实际已经请求成功了),在重试的情况下也会产生重复的数据。
3.使用浏览器的历史记录进行重复提交
4.定时任务重复执行的情况
5.还有就是MQ消息队列的情况下,消费者从队列中拿消息进行消费,如果队列中出现重复的消息,那么消费者在消费的过程过也避免不了重复进行消费,导致业务数据重复。
幂等性
主要是指我们在请求接口多次结果都一样的,这种情况在分布式系统中比较常见,声明接口的幂等性是由为重要的,可以避免我们产生很多重复的脏数据。
接口保证幂等性的方法
全局唯一ID
使用全局唯一ID,后端来判断请求是否重复,在并发的时候只能处理一个请求,其他相同的请求要么返回请求重复,要么进行等待。
状态机幂等
状态机幂等,后端接收到请求,通过状态来判断,拿创建订单的例子来说,假如订单的状态分别有 1.待处理 2.待支付 3.待出单4.已完成 5.已取消 正常会根据传进来ID和status作为条件进行查询
例如:update order set status =4 wehre id=123456 and status =3
如果影响行数为1的证明是第一次请求,如果sql返回影响行数为0则证明是重复请求,此时订单的状态已经变成已完成,接口直接返回订单已完成即可。
唯一索引
当出现重复插入的时候,由于数据表字段设置唯一索引,所以插入会报错,后端提醒重复插入数据。
insert前先select
操作提交前先进行查询,符合要求在进行查询,这样做只限于在单服务的情况下使用,缺点也是有的,每次要多一次查询并且在分布式的环境下是无法保证幂等性的。
异步缓冲队列
将所有的请求的接收下,放入缓冲队列,过滤掉重复请求,然后在异步执行请求,缺点是无法实时同步请求,必须在缓冲队列处理完后,轮询返回结果,异步的情况下,可以考虑使用此方法。
redis分布式锁
如果是分布式系统,可以在业务进行插入或者是更新的时候,可以通过ID+后缀获取分布式锁,同时的情况下只能有一个服务获取到锁,执行完成功后,在进行释放锁。
数据库乐观锁
所谓的数据库乐观锁就是通过version版本号字段,如果数据进行修改可以通过执行以下sql
update table set version =version+1 where id =#{id} and version =1
如果返回的影响行数是大于0的,那么表示更新成功,此时version=2,如果此时也有同样的请求进来where条件version!=1则无法执行更新,一定是重复请求,由此可看乐观锁也能保证幂等性。
Token机制
Token在处理接口的幂等性和防重性的时候是两种逻辑设计。
- Token幂等性 1.客户端先进行获取token —>生成token—>放入redis中并设置过期时间 2.用户请求携带token请求接口—>redis判断token是否存在—存在直接进行数据库insert or update ,反之直接返回请求成功。
- Token防重性 客户端请求携带token,这里有个比较坑的点是:
先删除token
如果我在处理业务之前删除token,那么业务处理失败了,客户端也没有收到明确的返回,进行重试,此时token已经被删除,此时服务端判断token不存在然,认为重复请求直接返回,无法进行业务处理。
后删除token
如果在处理业务之后删除token,我们的业务正常处理成功,此时删除token失败,那么下一次请求进来如果还携带这个token那么就会造成重复请求,这里就涉及到缓存和数据库不一致的问题了,就要考虑删除缓存重试机制了。 缺点:缓存删除失败,会有额外请求获取token、删除token性能方面也会有影响。
Mq重复消费情景
Mq的场景下分为生产者发送消息重复、消费消费消息重复
生产者发送消息重复
生产者发送消息给mq,mq确认消息的时候出现了网络延迟的问题,实际mq已经收到消息,没有及时给生产者回应。 生产者可以采用定时任务进行消息重发,由于各种因素生产者是无法避免发送消息重复问题的。
消费消费消息重复
1.通过设置消费者的每次消费数,并发消费者实例中每个实例拉取的消息数量设置为1
container.setPrefetchCount(1);
2.唯一索引同接口幂等一样
3.将消息ID存入redis,每次消费前去redis获取有没有该消费记录,如果有则是重复消费,直接返回。
总结
可见在开发过程中接口的幂等性、防重性还是由为重要的,比如支付接口,如果重复支付导致多次扣钱…;创建订单导致订单重复,领导不请你喝茶才怪,本文就讲了一些做接口幂等性的一些常规做法,还有很多做法希望有过这方面设计经验的兄弟们指出不足,提供更多好的方法在评论区,感谢🙏。