java面试---消息中间件之kafka、activemq、ribbitmq、rocketmq详解与区别

目录

 

首先我们说说为什么要使用队列,什么情况下才会使用队列?

如何保证kafka的高可用性

kafka是如何保证消息不被重复消费的

Kafka如何保证消息的可靠性传输

消息队列的两种模式及kafka、activemq、ribbitmq的俩种模式区别

kafka、activemq、ribbitmq、rocketmq的区别

消息会有消费失败的情况吗;消费失败系统怎么处理;

kafka的底层是怎么实现的;怎么可以达到这么高的吞吐量

你们用kafka有遇到什么问题吗;

kafka如何保证发的消息是有序的

kafka和activemq和ribbitmq的区别

如果消费端异常,后续的操作流程是什么;

死信队列如果消息失败可以放;mq的更新频路是怎么设置的;


首先我们说说为什么要使用队列,什么情况下才会使用队列?

实时性要求不高,且比较耗时的任务,是队列的最佳应用场景。

比如说我在某网站注册一个账号,当我的信息入库注册成功后,网站需要发送一封激活邮件,让我激活账号,而这个发邮件的操作并不是需要实时响应的,没有必要卡在那个注册界面,等待邮件发送成功,再说发送邮件本来就是一个耗时的操作(需要调用第三方smtp服务器),此时,选择消息队列去处理。注册完成,我只要向队列投递一个消息,消息的内容中包含我要发送邮件的一些设置,以及发送时间,重试次数等消息属性。这里的投递操作(可以是入库,写入缓存等)是要消息进入一个实体的队列。其中应该有一进程(消费者)一直在后台运行,他不断的去轮训队列中的消息(按照时间正序,队列是先进先出),看有没有达到执行条件的,如果有就取出一条,根据消息配置,执行任务,如果成功,则销毁这条消息,继续轮训,如果失败,则重试,知道达到重试次数。这时用户已经收到注册成功的提示,但是已经去做其他事了,邮件也来了,用户点击邮件,注册成功。这就是消息队列的一个典型应用。
再说一个场景,点赞,这个在高并发的情况下,很容易造成数据库连接数占满,到时整个网站响应缓慢,才是就是想到要解决数据库的压力问题,一般就是两种方案,一是提高数据库本身的能力(如增加连接数,读写分离等),但是数据库总是有极限的,到达了极限是没有办法在提升了的,此时就要考虑第二种方案,释放数据库的压力,将压力转移到缓存里面。就拿实际的点赞来说吧,用户的点赞请求到来,我只是将点赞请求投递到消息队列里面,后续的点赞请求可以将消息合并,即只更新点赞数,不产生新的任务,此时有个进行再不断的轮训消息队列,将点赞消息消耗,并将值更新到数据库里面,这样就有效的降低了数据库的压力,因为在缓存层将数个数据库更新请求合并成一个,大大提高了效率,降低了负载。

Java消息服务(Java Message Service,JMS)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
点对点与发布订阅最初是由JMS定义的。这两种模式主要区别或解决的问题就是发送到队列的消息能否重复消费(多订阅)

如何保证kafka的高可用性

(1)kafka的一个基本架构:多个broker组成,一个broker是一个节点;你创建一个topic,这个topic可以划分成多个partition,每个partition可以存在于不同的broker上面,每个partition存放一部分数据。这是天然的分布式消息队列。

实际上rabbitmq并不是分布式消息队列,他就是传统的消息队列,只不过提供了一些集群、HA的机制而已,因为无论如何配置,rabbitmq一个queue的数据就存放在一个节点里面,镜像集群下,也是每个节点都放这个queue的全部数据。

kafka在0.8以前是没有HA机制的,也就是说任何一个broker宕机了,那个broker上的partition就丢了,没法读也没法写,没有什么高可用可言。

kafka在0.8之后,提过了HA机制,也就是replica副本机制。每个partition的数据都会同步到其他机器上,形成自己的replica副本。然后所有的replica副本会选举一个leader出来,那么生产者消费者都和这个leader打交道,其他的replica就是follower。写的时候,leader会把数据同步到所有follower上面去,读的时候直接从leader上面读取即可。
为什么只能读写leader:因为要是你可以随意去读写每个follower,那么就要关心数据一致性问题,系统复杂度太高,容易出问题。kafka会均匀度讲一个partition的所有数据replica分布在不同的机器上,这样就可以提高容错性。
这样就是高可用了,因为如果某个broker宕机 了,没事儿,那个broker的partition在其他机器上有副本,如果这上面有某个partition的leader,那么此时会重新选举出一个现代leader出来,继续读写这个新的leader即可。
kafka高可用架构示意图.png
写消息: 写数据的时候,生产者就写leader,然后leader将数据落到磁盘上之后,接着其他follower自己主动从leader来pull数据。一旦所有follower同步好了数据,就会发送ack个leader,leader收到了所有的follower的ack之后,就会返回写成功的消息给消息生产者。(这只是一种模式,可以调整)。
读数据:消费数据的时候,只会从leader进行消费。但是只有一个消息已经被所有follower都同步成功返回ack的时候,这个消息才会被消费者读到。

kafka是如何保证消息不被重复消费的

一、kafka自带的消费机制

  kafka有个offset的概念,当每个消息被写进去后,都有一个offset,代表他的序号,然后consumer消费该数据之后,隔一段时间,会把自己消费过的消息的offset提交一下,代表我已经消费过了。下次我要是重启,就会继续从上次消费到的offset来继续消费。

  但是当我们直接kill进程了,再重启。这会导致consumer有些消息处理了,但是没来得及提交offset。等重启之后,少数消息就会再次消费一次。

  其他MQ也会有这种重复消费的问题,那么针对这种问题,我们需要从业务角度,考虑它的幂等性。

 

二、通过保证消息队列消费的幂等性来保证

  举个例子,当消费一条消息时就往数据库插入一条数据。如何保证重复消费也插入一条数据呢?

  那么我们就需要从幂等性角度考虑了。幂等性,我通俗点说,就一个数据,或者一个请求,无论来多次,对应的数据都不会改变的,不能出错。

 

怎么保证消息队列消费的幂等性?

我们需要结合业务来思考,比如下面的例子:

  1.比如某个数据要写库,你先根据主键查一下,如果数据有了,就别插入了,update一下好吧

  2.比如你是写redis,那没问题了,反正每次都是set,天然幂等性

  3.对于消息,我们可以建个表(专门存储消息消费记录)

    生产者,发送消息前判断库中是否有记录(有记录说明已发送),没有记录,先入库,状态为待消费,然后发送消息并把主键id带上。

    消费者,接收消息,通过主键ID查询记录表,判断消息状态是否已消费。若没消费过,则处理消息,处理完后,更新消息记录的状态为已消费。

Kafka如何保证消息的可靠性传输

1.消费端弄丢了数据

唯一可能导致消费者弄丢数据的情况,就是说,你消费到了这个消息,然后消费者那边自动提交了 offset,让 Kafka 以为你已经消费好了这个消息,但其实你才刚准备处理这个消息,你还没处理,你自己就挂了,此时这条消息就丢咯。

这不是跟 RabbitMQ 差不多吗,大家都知道 Kafka 会自动提交 offset,那么只要关闭自动提交 offset,在处理完之后自己手动提交 offset,就可以保证数据不会丢。但是此时确实还是可能会有重复消费,比如你刚处理完,还没提交 offset,结果自己挂了,此时肯定会重复消费一次,自己保证幂等性就好了。

生产环境碰到的一个问题,就是说我们的 Kafka 消费者消费到了数据之后是写到一个内存的 queue 里先缓冲一下,结果有的时候,你刚把消息写入内存 queue,然后消费者会自动提交 offset。然后此时我们重启了系统,就会导致内存 queue 里还没来得及处理的数据就丢失了。

2.Kafka 弄丢了数据

这块比较常见的一个场景,就是 Kafka 某个 broker 宕机,然后重新选举 partition 的 leader。大家想想,要是此时其他的 follower 刚好还有些数据没有同步,结果此时 leader 挂了,然后选举某个 follower 成 leader 之后,不就少了一些数据?这就丢了一些数据啊。

生产环境也遇到过,我们也是,之前 Kafka 的 leader 机器宕机了,将 follower 切换为 leader 之后,就会发现说这个数据就丢了。

所以此时一般是要求起码设置如下 4 个参数:

  • 给 topic 设置 replication.factor 参数:这个值必须大于 1,要求每个 partition 必须有至少 2 个副本。
  • 在 Kafka 服务端设置 min.insync.replicas 参数:这个值必须大于 1,这个是要求一个 leader 至少感知到有至少一个 follower 还跟自己保持联系,没掉队,这样才能确保 leader 挂了还有一个 follower 吧。
  • 在 producer 端设置 acks=all:这个是要求每条数据,必须是写入所有 replica 之后,才能认为是写成功了。
  • 在 producer 端设置 retries=MAX(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试,卡在这里了。

我们生产环境就是按照上述要求配置的,这样配置之后,至少在 Kafka broker 端就可以保证在 leader 所在 broker 发生故障,进行 leader 切换时,数据不会丢失。

3. 生产者会不会弄丢数据?

如果按照上述的思路设置了 acks=all,一定不会丢,要求是,你的 leader 接收到消息,所有的 follower 都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次。

消息队列的两种模式及kafka、activemq、ribbitmq的俩种模式区别

JMS规范目前支持两种消息模型:点对点(point to point, queue)和发布/订阅(publish/subscribe,topic)。

1.1、点对点:Queue,不可重复消费

消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。
消息被消费以后,queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息。Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。

1.2、发布/订阅:Topic,可以重复消费

消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。

支持订阅组的发布订阅模式:
发布订阅模式下,当发布者消息量很大时,显然单个订阅者的处理能力是不足的。实际上现实场景中是多个订阅者节点组成一个订阅组负载均衡消费topic消息即分组订阅,这样订阅者很容易实现消费能力线性扩展。可以看成是一个topic下有多个Queue,每个Queue是点对点的方式,Queue之间是发布订阅方式。

2、区别

2.1、点对点模式

生产者发送一条消息到queue,一个queue可以有很多消费者,但是一个消息只能被一个消费者接受,当没有消费者可用时,这个消息会被保存直到有 一个可用的消费者,所以Queue实现了一个可靠的负载均衡。

2.2、发布订阅模式

发布者发送到topic的消息,只有订阅了topic的订阅者才会收到消息。topic实现了发布和订阅,当你发布一个消息,所有订阅这个topic的服务都能得到这个消息,所以从1到N个订阅者都能得到这个消息的拷贝。
 

3、流行模型比较

传统企业型消息队列ActiveMQ遵循了JMS规范,实现了点对点和发布订阅模型,但其他流行的消息队列RabbitMQ、Kafka并没有遵循JMS规范。

3.1、RabbitMQ

RabbitMQ实现了AQMP协议,AQMP协议定义了消息路由规则和方式。生产端通过路由规则发送消息到不同queue,消费端根据queue名称消费消息。
RabbitMQ既支持内存队列也支持持久化队列,消费端为推模型,消费状态和订阅关系由服务端负责维护,消息消费完后立即删除,不保留历史消息。

(1)点对点
生产端发送一条消息通过路由投递到Queue,只有一个消费者能消费到。


(2)多订阅
当RabbitMQ需要支持多订阅时,发布者发送的消息通过路由同时写到多个Queue,不同订阅组消费不同的Queue。所以支持多订阅时,消息会多个拷贝。

3.2、Kafka

Kafka只支持消息持久化消费端为拉模型,消费状态和订阅关系由客户端端负责维护,消息消费完后不会立即删除,会保留历史消息。因此支持多订阅时,消息只会存储一份就可以了。但是可能产生重复消费的情况

(1)点对点&多订阅
发布者生产一条消息到topic中,不同订阅组消费此消息。

kafka、activemq、ribbitmq、rocketmq的区别

1.单机吞吐量

ActiveMQ:万级,吞吐量比RocketMQ和Kafka要低一个数量级。
RabbitMQ:万级,吞吐量比RocketMQ和Kafka要低一个数量级。
RocketMQ:10万级,RocketMQ也是可以支撑高吞吐的一种MQ。
Kafka:10万级,这是Kafka最大的优点,就是吞吐量高;一般配合大数据类的系统来进行实时数据基数按、日志采集等场景。

2.Topic数量对吞吐量的影响

ActiveMQ:
RabbitMQ:
RocketMQ:topic可以达到几百,几千个的级别,吞吐量会有较小幅度的下降;这是RocketMQ的一大优势,在同等机器下,可以支撑大量的topic。
Kafka:topic从几十个到几百个的时候,吞吐量会大幅度下降所以在同等机器下,kafka尽量保证topic数量不要过多。如果要支撑大规模topic,需要增加更多的机器资源。

3.时效性

ActiveMQ:ms级;
RabbitMQ:微妙级,这是Rabbitmq的一大特点,延迟是最低的;
RocketMQ:ms级;
Kafka:在ms级内。

4.可用性

ActiveMQ:高,基于主从架构实现高可用性;
RabbitMQ:高,基于主从架构实现高可用性;
RocketMQ:非常高,分布式架构;
Kafka:非常高,Kafka是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用。

5.消息可靠性

ActiveMQ:有较低的概率丢失数据;
RabbitMQ:
RocketMQ:参数经过优化配置,可以做到0丢失;
Kafka:参数经过优化配置,可以做到0丢失;

6.功能支持

ActiveMQ:MQ领域的功能及其完备;
RabbitMQ:基于erlang语言开发,并发性能极其好,延时很低;
RocketMQ:MQ功能较为完善,还是分布式的,扩展性能好;
Kafka:功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用,是事实上的标准;

ActiveMQ社区也不是很活跃。 Kafka 是业内标准的,绝对没问题,社区活跃度很高

activemq因为他是用java开发的,不支持跨语言的这种形式。ribbitmq的话他主要是用在事务级别的,对大数据的处理性能比较弱一点。

ActiveMQ:

1.非常成熟,功能强大,在业内大量的公司以及项目都有应用;
2.偶尔会有较低概率丢失消息;
3.现在社区以及国内应用都越来越少,官方社区现在对ActiveMQ5.x维护越来越少,几个月才发布一个版本;
4.确实主要是基于解耦和异步来用的,较少在大规模吞吐的场景中使用。

RabbitMQ:

1.erlang语言开发的,性能极其好,延时很低;
2.吞吐量到万级,MQ功能比较完备;
3.开源提供管理界面非常棒,用起来很好用;
4.社区很活跃,几乎每个月发布几个版本;
5.国内一些互联网公司近几年用RabbitMQ比较多一些;
6.问题是RabbitMQ的吞吐量也会低一些,这是应为它做的实现机制比较重;
7.而且erlang开发,国内有几个公司有实力做erlang源码级别的研究和定制?如果说你没这个实力的话,确实偶尔会有一些问题,很难去看懂源码,公司对这个东西的掌控很弱,基本只能依赖于开源社区的快速维护和修复bug;
8.而且Rabbitmq集群动态扩展会很麻烦,不过这个我觉得还好。其实主要是erlang语言本身带来的问题。很难读源码,很难定制和掌控。

RocketMQ:

1.接口简单易用,而且毕竟在阿里大规模应用过,有阿里品牌保障;
2.日处理消息上百亿之多,可以做到大规模吞吐,性能也非常好,分布式扩展也很方便,社区维护还可以,可靠性和可用性都是ok的,还可以支撑大规模的topic数量,支持复杂MQ业务场景;
3.而且一个很大的优势在于,阿里出品都是java系的,我们可以自己阅读源码,定制自己公司的MQ,可以掌控;
4.社区活跃度相对较为一般,不过也还可以,文档相对来说简单一些,然后接口这块不是按照标准JMS规范走的有些系统要迁移需要修改大量代码;
5.还有就是阿里出台的技术,你得做好这个技术万一被抛弃,社区黄掉的风险,那如果你们公司有技术实力我觉得用RocketMQ挺好的;

Kafka:

1.kafka的特点其实很明显,就是仅仅提供较少的核心功能,但是提供超高的吞吐量,ms级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展;
2.同时kafka最好是支撑较少的topic数量即可,保证其超高吞吐量;
3.而且kafka唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略;
4.这个特性天然适合大数据实时计算以及日志收集。

消息会有消费失败的情况吗;消费失败系统怎么处理;

kafka的底层是怎么实现的;怎么可以达到这么高的吞吐量

他的话 底层也是使用的多路复用的这种形式 就是我作为一个数据的提供者我去发消息 那么他的话把topic分成一个个postion然后提高并发量所以他的一个性能的话 其实也跟这个有很大的关系 其实postion底层的话 数据的存储也是根据.log文件 跟日志也比较容易混淆 里面的话使用sengment对底层存储分段就是它里面有一个偏移量offset 然后发送他kafka服务器brock上 我们要进行一个存储 我们会根据我们的业务场景进行一个设计来保证我们数据丢失的一个情况 他的话我们平常的话也会设置成为1的一个情况 1的话就是我当前的一个lander接收到就行了 -1的话可能是这个lrp里面所有的从节点都接受到这个数据之后我们才给反馈  客户这边的话发数据也会分这种buffer  我们会给buffer定义一个长度 还有就是分批的一个处理 我给他每次手机达到一个batch  也会去设计一个批处理的量 达到这个数据量之后每一个时间段我给他发送  想这些都可以提高性能

你们用kafka有遇到什么问题吗;

kafka几个基本的概念:
broker: 消息处理结点,多个broker组成kafka集群。
topic: 一类消息,如page view,click行为等。
partition: topic的物理分组,每个partition都是一个有序队列。
replica:partition 的副本,保障 partition 的高可用。
producer: 产生信息的主体,可以是服务器日志信息等。
consumer: 消费producer产生话题消息的主体。
Consumer group:high-level consumer API 中,每个 consumer 都属于一个 consumer group,每条消息只能被 consumer group 的一个 Consumer 消费,但可以被多个 consumer group 消费。
segment: 多个大小相等的段组成了一个partition。
offset: 一个连续的用于定位被追加到分区的每一个消息的序列号,最大值为64位的long大小,19位数字字符长度。
massage: kafka中最基本的传递对象,有固定格式。
zookeeper:kafka 通过 zookeeper 来存储集群的 meta 信息。
controller:kafka 集群中的其中一个服务器,用来进行 leader election 以及 各种 failover。
partition、segment、offset都是为topic服务的,每个topic可以分为多个partition,一个partition相当于一个大目录,每个partition下面有多个大小相等的segment文件,这个segment是由message组成的,而每一个的segment不一定由大小相等的message组成。segment大小及生命周期在server.properties文件中配置。offset用于定位位于段里的唯一消息,这个offset用于定位partition中的唯一性。

kafka如何保证发的消息是有序的

客户端获取到的每一个postion的数据  其实都是有序的 但是我们客户端这边可能说是并发的这种情况那我们会给他加一个内存优化之类的

方案一,kafka topic 只设置一个partition分区  

方案二,producer将消息发送到指定partition分区

解析:

方案一:kafka默认保证同一个partition分区内的消息是有序的,则可以设置topic只使用一个分区,这样消息就是全局有序,缺点是只能被consumer group里的一个消费者消费,降低了性能,不适用高并发的情况

方案二:既然kafka默认保证同一个partition分区内的消息是有序的,则producer可以在发送消息时可以指定需要保证顺序的几条消息发送到同一个分区,这样消费者消费时,消息就是有序。

kafka和activemq和ribbitmq的区别

activemq因为他是用java开发的,不支持跨语言的这种形式。ribbitmq的话他主要是用在事务级别的,对大数据的处理性能比较弱一点。

如果消费端异常,后续的操作流程是什么;

死信队列如果消息失败可以放;mq的更新频路是怎么设置的;

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值