持久性
一些下单任务,例如付完钱后异步下单,为啥我们主流都是使用mq来做异步,而不是使用线程池来做异步呢?明明都能做异步
因为如果我们的Java程序宕机了,那么我们的异步任务没完成完,那就直接结束了
如果我们使用mq的话,它是Broker,它有持久化功能,文件可以存储到MQ内部,本地文件持久化
这样子我们就能重试,保证消息消费
可靠性
mq有重试机制,如果消息发送失败的话我们可以重试,而用线程池这种的话要写一对冗余的代码
我们保证消息能成功发送
可靠性体现在三个地方
确保生产者发送给Broker
失败场景:网络波动或者mq宕机等,导致发送不过去
确保Broker发送给消费者
失败场景:网络波动或者mq宕机等,导致发送不过去
确保保证消费者能成功消费到消息
默认:发送到消费者mq就标记消费成功,也就是成功发送到消费者就直接ack
失败场景:我们可能消息队列发送给消费者成功,但是java程序没执行完代码就宕机的,但是此时我的消息队列已经发送消息了,所以它会结束表示成功消费,但是其实我们java程序没运行完,本质上还是消费失败。
所以我们有一个手动ack机制,也就是我们执行完,再返回一个ack告诉消息队列我们发送成功了
不然我们就重试3次(代码要自己写)
我拿RabbitMq举例子,可以看一下我的其他文章
RabbitMq手动ack的超简单案例+Confirm和Return机制的配置和使用_rabbitmq 手动ack-CSDN博客
幂等性
重复消费问题
幂等性就是保证消息的唯一性,防止消息被重复消费
可能因为网络故障波动或者其他原因,导致我们的mq重复发送多次请求给消费者,导致我们的消费者重复消费。
如果是下单那么我们的单号可能有重复的5个,这样子明显是不行的
解决方案
我们要弄消息的唯一ID(唯一标识)
例如把消息的唯一标识改成UUID+时间戳,那么怎么样都不会遇到ID相同的情况
为什么要这样弄呢?
我拿RabbitMq举例子,RabbitMq的消息的唯一ID是通过队列名,交换机名和消息内容生成的。
那我们发送消息内容相同,那么我们的自动生成的ID不就一样了?
所以我们要改一下我们的ID生成策略
然后我们把这个ID存到Redis里面去,每次消费前去判断Redis里面是否存在这个ID。
如果有那么就消费过了,我们不执行,防止重复下单。
如果没有,那么就是我们没消费过,我们直接下单就行了。
这样子就保证了消息的幂等性
这三个性质是共通的,可以延伸到其他的消息队列,例如Kafka和RocketMq