背景 & 问题
本位柯苏远写于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:解决思路挺直观的,因为会往数据库插入重复数据,所以我们给每个消息一个唯一id,当这个消息插入数据库之后,这个消息唯一id也存入了,然后对这个消息唯一id字段进行设置唯一键。这样在数据库层面就避免了消息存储重复的可能。
针对问题2:推送接口超时,不推的话,大部分业务是接受不了的,因为消息丢了,所以我们继续推一次这个消息,不用担心会重复发送,因为在存储层面我们用了消息唯一id做唯一键。
疑问
消息的唯一id怎么产生?
唯一id有很多种,用uuid,或者雪花算法都可以,我们这里用的是uuid。
将一个消息和一个uuid绑定的时候,当超时之后,这个消息对应的uuid要不要改?
肯定是不能改的,改了的话,在数据库层面就会重复插入同一条消息
那么不能改的话,当这个消息推送超时,如何解决继续用这个uuid推送?
其实很简单,让uuid变成这个消息的一个属性,然后推送超时,将这个消息弄到一个本地的队列里去,进行重试。一定要注意,每个消息的唯一id是不会变的。
后续安排
这个是我目前在工作中接触的第一个幂等场景,后续再看看其它幂等情况。
总结
- 消息重复发送的可能性。
- 引入消息唯一id在数据库层面做唯一索引。
- 消息推送超时,在内存进行重试,消息的唯一id是和每个消息强绑的,也就是说再重试的时候不能给消息生成新的唯一id