加入收藏 | 设为首页 | 会员中心 | 我要投稿 宁德站长网 (https://www.0593zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

一个天天用消息队列的人,不知道为啥用 MQ,这就有点尴尬

发布时间:2019-08-17 01:02:53 所属栏目:优化 来源:java互联网架构
导读:副标题#e# 1、为什么要使用消息队列? 分析:一个用消息队列的人,不知道为啥用,有点尴尬。没有复习这点,很容易被问蒙,然后就开始胡扯了。 回答:这个问题,咱只答三个最主要的应用场景(不可否认还有其他的,但是只答三个主要的),即以下六个字:解耦、异步、

多master多slave模式部署架构图(网上找的,偷个懒,懒得画):

一个天天用消息队列的人,不知道为啥用 MQ,这就有点尴尬

其实博主第一眼看到这个图,就觉得和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的拓补架构图(也是找的,懒得画)

一个天天用消息队列的人,不知道为啥用 MQ,这就有点尴尬

如上图所示,一个典型的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订阅并消费消息。

至于rabbitMQ,也有普通集群和镜像集群模式,自行去了解,比较简单,两小时即懂。

要求,在回答高可用的问题时,应该能逻辑清晰的画出自己的MQ集群架构或清晰的叙述出来。

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

分析:这个问题其实换一种问法就是,如何保证消息队列的幂等性?

这个问题可以认为是消息队列领域的基本问题。换句话来说,是在考察你的设计能力,这个问题的回答可以根据具体的业务场景来答,没有固定的答案。

回答:先来说一下为什么会造成重复消费?

其实无论是那种消息队列,造成重复消费原因其实都是类似的。

正常情况下,消费者在消费消息时候,消费完毕后,会发送一个确认信息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。只是不同的消息队列发送的确认信息形式不同

例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,kafka实际上有个offset的概念

简单说一下(如果还不懂,出门找一个kafka入门到精通教程),就是每一个消息都有一个offset,kafka消费过消息后,需要提交offset,让消息队列知道自己已经消费过了。

那造成重复消费的原因?

就是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将该消息分发给其他的消费者。

如何解决?这个问题针对业务场景来答分以下几点

(1)比如,你拿到这个消息做数据库的insert操作。

那就容易了,给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。

(2)再比如,你拿到这个消息做redis的set的操作

那就容易了,不用解决。因为你无论set几次结果都是一样的,set操作本来就算幂等操作。

(3)如果上面两种情况还不行,上大招。

准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。

6、如何保证消费的可靠性传输?

分析:我们在使用消息队列的过程中,应该做到消息不能多消费,也不能少消费。如果无法做到可靠性传输,可能给公司带来千万级别的财产损失。

同样的,如果可靠性传输在使用过程中,没有考虑到,这不是给公司挖坑么,你可以拍拍屁股走了,公司损失的钱,谁承担。

还是那句话,认真对待每一个项目,不要给公司挖坑

回答:其实这个可靠性传输,每种MQ都要从三个角度来分析:生产者弄丢数据、消息队列弄丢数据、消费者弄丢数据

RabbitMQ

(1)生产者丢数据

从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息。

transaction机制就是说,发送消息前,开启事物(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事物就会回滚(channel.txRollback()),如果发送成功则提交事物(channel.txCommit())。

然而缺点就是吞吐量下降了。因此,按照博主的经验,生产上用confirm模式的居多。

一旦channel进入confirm模式,所有在该信道上面发布的消息都将会被指派一个唯一的ID(从1开始)

一旦消息被投递到所有匹配的队列之后,rabbitMQ就会发送一个Ack给生产者(包含消息的唯一ID)

这就使得生产者知道消息已经正确到达目的队列了.如果rabiitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。

处理Ack和Nack的代码如下所示(说好不上代码的,偷偷上了):

  1. channel.addConfirmListener(new ConfirmListener() { 
  2.  @Override 
  3.  public void handleNack(long deliveryTag, boolean multiple) throws IOException { 
  4.  System.out.println("nack: deliveryTag = "+deliveryTag+" multiple: "+multiple); 
  5.  } 
  6.  @Override 
  7.  public void handleAck(long deliveryTag, boolean multiple) throws IOException { 
  8.  System.out.println("ack: deliveryTag = "+deliveryTag+" multiple: "+multiple); 
  9.  } 
  10. }); 

(2)消息队列丢数据

处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。

这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。

这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。

那么如何持久化呢,这里顺便说一下吧,其实也很容易,就下面两步

1、将queue的持久化标识durable设置为true,则代表是一个持久的队列

2、发送消息的时候将deliveryMode=2

这样设置以后,rabbitMQ就算挂了,重启后也能恢复数据

(3)消费者丢数据

消费者丢数据一般是因为采用了自动确认消息模式。

这种模式下,消费者会自动确认收到信息。这时rahbitMQ会立即将消息删除,这种情况下如果消费者出现异常而没能处理该消息,就会丢失该消息。

至于解决方案,采用手动确认消息即可。

kafka

一个天天用消息队列的人,不知道为啥用 MQ,这就有点尴尬

Producer在发布消息到某个Partition时,先通过ZooKeeper找到该Partition的Leader

然后无论该Topic的Replication Factor为多少(也即该Partition有多少个Replica),Producer只将该消息发送到该Partition的Leader。

Leader会将该消息写入其本地Log。每个Follower都从Leader中pull数据。

针对上述情况,得出如下分析

(1)生产者丢数据

在kafka生产中,基本都有一个leader和多个follwer。follwer会去同步leader的信息。

(编辑:宁德站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!