RabbitMQ学习(三):高级特性

1 生产端如何可靠地投递消息

1.1 消息落库打标

消息落库,操作消息的状态位,然后利用定时任务轮询发送失败的消息并重试,但是不适合高并发场景。

  • 第一步:生产者将消息写入数据库。
  • 第二步:生产者向MQ发送消息。
  • 第三步:生产者监听消息发送状态(confirm)。
  • 第四步:生产者更新消息的状态位。
  • 第五步:启动一个分布式定时任务轮询发送失败的消息,并重新发送。

1.2 消息延迟投递,通过二次确认回调检查。

  • 第一步:生产者将消息写入数据库。
  • 第二步:生产者第一次向MQ发送消息。
  • 第三步:生产者第二次延迟向MQ发送消息(另一个队列)。
  • 第四步:消费者消费并生成一条新的确认消息,发送往MQ。
  • 第五步:另一个异步服务监听消费者的确认消息。
  • 第六步:异步服务将消息写入数据库。
  • 第七步:异步服务在另一个队列中收到了生产者发送的延迟消息。然后查询数据库确认是否成功处理,如果数据库中没有记录,就向生产者发送一个重新发送请求,重新发送该消息。

2 生产者确认(Confirm消息确认机制)

生产者投递消息后,如果Broker收到消息,就会给生产者一个应答,生产者接收到应答后就可以确认这条消息已正常发送到MQ中。

3 消息的幂等性保障

3.1 唯一ID + 指纹码 机制

根据唯一ID + 指纹码去数据库中查询是否有对应的记录,如果有则不处理消息到的消息。
好处:简单易实现,坏处:频繁操作数据库,有性能瓶颈。
解决方案:利用ID进行分库分表策略,进行算法路由,分摊数据库压力。

3.2 利用Redis的原子性实现

这个实现很简单,但是需要考虑以下两个问题:

  • 如果数据落库的话,需要解决数据库和缓存之间的原子性问题。
  • 如果数据不落库,应该设置怎样的定时同步策略。

4 Return消息机制

ReturnListener用于监听一些不可路由的消息,比如exchange不存在或者制定的routingkey路由不到。
主要涉及的配置项为mandatory

  • 为true,则生产者可以通过ReturnListener监听到,然后进行后续的处理。
  • 为false,则直接丢弃消息。
    与此同时,如果交换机将消息路由到队列时,发现队列上没有任何消费者,也会根据参数immediate做处理,不过该参数已经废弃了。

5 消息的分发与消费端限流

5.1 消息的分发

队列是以轮询的方式将消息分发给消费者。比如现在有n个消费者,会将第m条消息分发给m % n个消费者。

5.2 消费端的限流

通过channel.basicQos(int prefetchSize, int prefetchCount, boolean global)方法实现:

  • prefetchSize用于限制消费者所能接收未确认消息的数量。一般默认为0,表示无限制。
  • prefetchCount用于限制信道上的消费者所能保持的最大未确认消息的数量。
  • global,默认为false,如果为true,则信道上所有的消费者都需要受到prefetchCount的限制,如果为false,则信道上新的消费者需要受到prefetchCount的限制。
    注意:
  • autoAck设置为false。
  • 对拉模式无效。

6 消息的TTL

TTL是Time to Live的缩写,也就是生存时间,单位毫秒。

  • 在channel.queueDeclare方法中加入x-message-ttl参数,可以设置队列级别的过期时间。
  • 在channel.basicPublish方法中加入expiration属性,可以设置消息级别的过期时间。
  • 如果二者一起时间,则以消息的TTL二者之间较小的那个数值为准。
  • 推荐设置队列级别的过期时间,因为队列级别的过期消息处理只需要定期从队列头部开始扫描即可,而消息级别的过期消息处理需要扫描整个队列,性能低。

7 队列的TTL

  • 通过channel.queueDeclare方法中的x-expires参数设置队列被删除前空闲的时间。单位毫秒。
  • 如果RabbitMQ重启,持久化的队列的过期时间会被重新计算。
发布了8 篇原创文章 · 获赞 3 · 访问量 279

猜你喜欢

转载自blog.csdn.net/quanhong_ding/article/details/105420720
今日推荐