分布式开发之消息队列

Posted frank2015

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式开发之消息队列相关的知识,希望对你有一定的参考价值。

本文围绕如下几点进行阐述:

  1. 为什么使用消息队列?
  2. 使用消息队列有什么缺点?
  3. 消息队列如何选型?
  4. 如何保证消息队列是高可用的?
  5. 如何保证消息不被重复消费?
  6. 如何保证消费的可靠性传输?

 

1. 为什么使用消息队列?

    解耦,异步,限流

 

2. 使用消息队列有什么缺点?

   导致系统可用性降低,若MQ发生故障会影响系统可用性。系统复杂性增加,引入MQ会引入新的关注点,比如需要考虑消息可靠性投递,消息重复消费等问题。

 

3. 消息队列如何选型?

   1. 看MQ更新频率

   2. 看MQ性能

   3. 比较MQ各自特点,比如Kafka适合大数据场景如日志采集

 

4. 如何保证消息队列是高可用的?

 以RcoketMQ为例,他的集群就有多master 模式、多master多slave异步复制模式、多 master多slave同步双写模式。多master多slave模式部署架构图:

技术分享图片

Rocket的高可用架构和kafka很像,只是NameServer集群,在kafka中是用zookeeper代替,都是用来保存和发现master和slave用的。通信过程如下:
Producer 与 NameServer集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer 获取 Topic 路由信息,并向提供 Topic 服务的 Broker Master 建立长连接,且定时向 Broker 发送心跳。Producer 只能将消息发送到 Broker master,但是 Consumer 则不一样,它同时和提供 Topic 服务的 Master 和 Slave建立长连接,既可以从 Broker Master 订阅消息,也可以从 Broker Slave 订阅消息。

 

kafka:

技术分享图片

如上图所示,一个典型的Kafka集群中包含若干Producer(可以是web前端产生的Page View,或者是服务器日志,系统CPU、Memory等),若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干Consumer Group,以及一个Zookeeper集群。Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息。

 

5. 如何保证消息不被重复消费?

    其实无论是那种消息队列,造成重复消费原因其实都是类似的。正常情况下,消费者在消费消息时候,消费完毕后,会发送一个确认信息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息 队列中删除。只是不同的消息队列发送的确认信息形式不同,例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,kafka实际上有个offset的概念,简单说一下(如果还不懂,出门找一个kafka入门到精通教程),就是每一个消息都有一个offset,kafka消费过消息后,需要提交offset,让消息队列知道自己已经消费过了。那造成重复消费的原因?,就是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将该消息分发给其他的消费者。

1. 利用数据库主键唯一性:全局唯一ID + 指纹码。当唯一ID插入数据库时会因为主键约束而失败,这样就能保障消息不会被重复消费。

2. 利用redis的原子性。但也有需要考虑的问题。如何消息落地。

6.如何保障消费的可靠性传输。

   可靠性传输要从三个角度来分析:生产者弄丢数据、消息队列弄丢数据、消费者弄丢数据

   6.1  生产端的可靠性投递

         保障消息的成功发出,保障MQ节点的成功接收,发送端收到MQ节点确认应答。完善的消息进行补偿机制。

   6.1.1 解决方案

         6.1.1.1  消息落库,对消息状态进行打标。

技术分享图片

         6.1.1.2  消息的延迟投递,做二次确认,回调检查。

 技术分享图片

     6.2 MQ消息队列丢数据
          处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。
    那么如何持久化呢,这里顺便说一下吧,其实也很容易,就下面两步
    1、将queue的持久化标识durable设置为true,则代表是一个持久的队列
    2、发送消息的时候将deliveryMode=2
  这样设置以后,rabbitMQ就算挂了,重启后也能恢复数据

 

    6.3 消费者丢数据
          消费者丢数据一般是因为采用了自动确认消息模式。这种模式下,消费者会自动确认收到信息。这时RabbitMQ会立即将消息删除,这种情况下如果消费者出现异常而没能处理该消息,就会丢失该消息。
          至于解决方案,采用手动确认消息即可。

 

未完待续









以上是关于分布式开发之消息队列的主要内容,如果未能解决你的问题,请参考以下文章

分布式技术之 单线程消息队列 SingularUpdateQueue

消息队列之简要设计

消息队列之RabbitMQ

JavaWeb项目架构之Kafka分布式日志队列

JavaWeb项目架构之Kafka分布式日志队列

Python开发模块:Celery 分布式异步消息任务队列