消息幂等

背景 & 问题

本位柯苏远写于2024年4月18日22点15分

还是在公司的整个电商业务中要接入sms 短信发送功能。

上一篇文章地址:分布式锁实战 - 掘金 (juejin.cn)

这周到实际开发过程了,首先将整个需求的技术方案设计好了,然后进行开发。整个方案的设计图如下所示:

A服务:表示每个会产生sms消息的触发点,不单单是一个服务,而是很多个。 B服务:对外提供一个插入消息表的接口,所有触发发送sms短信的服务点都调用这个接口。 C服务:是一个轮询服务,对短信表进行轮询,找到待发送的sms 短信,然后调用sms api 发送短信给客户。

红框部分有个问题:

当A向B推送消息的时候,在B收到消息往数据库成功插入数据之后,当要给A服务返回成功标识的时候,由于网络问题接口超时了,A那边收到的返回就是接口超时,那么对于A服务来说我并不知道这个消息到底成功推送没有?A此时应该怎么做?如果重新推送的话可能会在数据库存入一个一模一样的消息。如果不重新推送的话,那消息可能就丢失了。 归根结底就是由于在第一步消息推送的时候如果发送接口超时,在A服务中我不知道如何处理当前超时的消息。

如何解决

再次重申问题:

  1. 如果推送消息接口超时,我按推送失败处理,继续推送这个消息,那么就可能往数据库插入两条一模一样的消息,导致的结果就是客户收到两条一模一样的消息。
  2. 如果推送消息接口超时,我按推送成功处理,不对这个消息再次推送,那么可能造成的问题就更大了,消息丢失了,这肯定不允许。

既然存在两个问题,那么我们就分别想解决方案。

针对问题1:解决思路挺直观的,因为会往数据库插入重复数据,所以我们给每个消息一个唯一id,当这个消息插入数据库之后,这个消息唯一id也存入了,然后对这个消息唯一id字段进行设置唯一键。这样在数据库层面就避免了消息存储重复的可能。

针对问题2:推送接口超时,不推的话,大部分业务是接受不了的,因为消息丢了,所以我们继续推一次这个消息,不用担心会重复发送,因为在存储层面我们用了消息唯一id做唯一键。

疑问

消息的唯一id怎么产生?

唯一id有很多种,用uuid,或者雪花算法都可以,我们这里用的是uuid。

将一个消息和一个uuid绑定的时候,当超时之后,这个消息对应的uuid要不要改?

肯定是不能改的,改了的话,在数据库层面就会重复插入同一条消息

那么不能改的话,当这个消息推送超时,如何解决继续用这个uuid推送?

其实很简单,让uuid变成这个消息的一个属性,然后推送超时,将这个消息弄到一个本地的队列里去,进行重试。一定要注意,每个消息的唯一id是不会变的。

后续安排

这个是我目前在工作中接触的第一个幂等场景,后续再看看其它幂等情况。

总结

  1. 消息重复发送的可能性。
  2. 引入消息唯一id在数据库层面做唯一索引。
  3. 消息推送超时,在内存进行重试,消息的唯一id是和每个消息强绑的,也就是说再重试的时候不能给消息生成新的唯一id
相关推荐
东风破_2 小时前
danci 2:创建的单词书到底存在哪里?从 Supabase 一路理解 ORM、Drizzle 和 RLS
数据库·后端·node.js
东风破_2 小时前
danci 项目(三):从 JSON 数据到 AI Coding,真实项目里的数据清洗、Prompt 和工程规范
前端·后端·node.js
东风破_3 小时前
danci 项目(一):从需求到架构,一个单词学习系统为什么会这样设计
前端·后端·node.js
jay神4 小时前
【计算机毕业设计】基于SpringBoot的程序教学辅助系统
java·前端·vue.js·spring boot·后端·毕业设计·课程设计
2601_962073817 小时前
Spring全面详解(基础版)
java·后端·spring
IT_陈寒7 小时前
Vite打包时的静态资源坑,我帮你踩过了
前端·人工智能·后端
考虑考虑7 小时前
kubectl命令
运维·后端·自动化运维
第五页的你8 小时前
ElasticSearch结合本地消息表实现搜索功能
后端
谢亮_vipxieliang8 小时前
ValidX分组验证详解:不同场景使用不同验证规则
java·spring boot·后端·spring cloud·eclipse·hibernate
超兔一体云8 小时前
MySQL索引优化实战——从慢查询到索引调优
后端·mysql