1,如何保证消息不被重复消费(幂等性)?

首先呢,不被重复消费其实就是如何实现消息去重的判断;去重就要给每条消息设置一个全局的唯一的id标识,可以利用雪花算法子类的;
那么如何实现去重呢?首先说说我第一时间想到的方案:
方案一:利用MySQL建立一张表,将每条消息的id设置为unique(唯一索引),每次消费消息前都去带着id对这张表插入数据,如果说插入成功了,就表明在表中这条数据是没有的,如果是插入失败则表明该条消息已经存在;然后不走消费消息的逻辑就好了,直接结束;但是呢这种方法有个缺点就是:只适用于低并发量的情况,在高并发场景中,会造成数据库压力过大;
方案二:其实方案二的大致原理也是和方案一类似,但是作为储存的数据库换成了Redis;这里利用到了Redis的全局锁,也就是setnx那个指令,在只有当消息不存在的时候才能将key set进去,至于数据的key值就能用当前消息的唯一id,然后使用redisTemplate的setIfAbabsent方法,在执行消费逻辑前将这个key存入,如果返回true则表示该消息是唯一的,否则被消费过,直接跳过消费逻辑即可;用redis的优势就是比mysql承载的并发量可以很高;

2,如何保证消息不丢失?如何保证消息可靠性?

就拿RabbitMQ来举例吧:
RabbitMQ针对消息传递过程中可能发生问题的各个地方,给出了针对性的解决方案:(四种情况)
可能出现的四种情况:

  • 生产者发送消息时可能因为网络问题导致消息没有到达交换机:
    • RabbitMQ提供了publisher confirm机制 (消费者确认机制)
      • 生产者发送消息后,可以编写ConfirmCallback函数
      • 消息成功到达交换机后,RabbitMQ会调用ConfirmCallback通知消息的发送者,返回ACK
      • 消息如果未到达交换机,RabbitMQ也会调用ConfirmCallback通知消息的发送者,返回NACK
      • 消息超时未发送成功也会抛出异常
  • 消息到达交换机后,如果未能到达队列,也会导致消息丢失:
    • RabbitMQ提供了publisher return机制
      • 生产者可以定义ReturnCallback函数
      • 消息到达交换机,未到达队列,RabbitMQ会调用ReturnCallback通知发送者,告知失败原因
  • 消息到达队列后,MQ(消息队列)宕机也可能导致丢失消息:
    • RabbitMQ提供了持久化功能,集群的主从备份功能
      • 消息持久化,RabbitMQ会将交换机、队列、消息持久化到磁盘,宕机重启可以恢复消息
      • 镜像集群,仲裁队列,都可以提供主从备份功能,主节点宕机,从节点会自动切换为主,数据依然在
  • 消息投递给消费者后,如果消费者处理不当,也可能导致消息丢失 :
    • SpringAMQP基于RabbitMQ提供了消费者确认机制、消费者重试机制,消费者失败处理策略:
      • 消费者的确认机制
        • 消费者处理消息成功,未出现异常时,Spring返回ACK给RabbitMQ,消息才被移除
        • 消费者处理消息失败,抛出异常,宕机,Spring返回NACK或者不返回结果,消息不被移除
      • 消费者重试机制
        • 默认情况下,消费者处理失败时,消息会再次回到MQ队列,然后投递给其它消费者。Spring提供的消费者重试机制,则是在处理失败后不返回NACK,而是直接在消费者本地重试。多次重试都失败后,则按照消费者失败处理策略来处理消息。避免了消息频繁入队带来的额外压力。
      • 消费者失败策略
        • 当消费者多次本地重试失败时,消息默认会丢弃。
        • Spring提供了Republish策略,在多次重试都失败,耗尽重试次数后,将消息重新投递给指定的异常交换机,并且会携带上异常栈信息,帮助定位问题。