消息队列常见总结

 文章目录:

        1.为什么要用消息队列

        2.使用消息队列带来的一些问题

        3.JMS VS AMQP

        4.常见的消息队列对比

        5. 引入消息队列之后如何保证高可用性

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

        7. 如何保证消息的可靠性传输(如何处理消息丢失的问题)?

        8. 我该怎么保证从消息队列里拿到的数据按顺序执行?

        9. 如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时, 说说怎么解决?

        10. 如果让你来开发一个消息队列中间件,你会怎么设计架构?


 1.为什么要用消息队列

                1.降低系统耦合度;

         消息发送者(生产者)和消息接受者(消费者)之间没有直接耦合,消息发送者将消息发送至 分布式消息队列即结束对消息的处理,消息接受者从分布式消息队列获取该消息后进行后续处理,并不需要知道该消 息从何而来。对新增业务,只要对该类消息感兴趣,即可订阅该消息,对原有系统和业务没有任何影响,从而实现网 站业务的可扩展性设计。

                2.通过异步处理提高系统性能

        在不使用消息队列服务器的时候,用户的请求数据直接写入数据库,在高并发的情况下数据库压力剧增,使 得响应速度变慢。但是在使用消息队列之后,用户的请求数据发送给消息队列之后立即 返回,再由消息队列的消费 者进程从消息队列中获取数据,异步写入数据库。由于消息队列服务器处理速度快于数据库(消息队列也比数据库有 更好的伸缩性),因此响应速度得到大幅改善。

                3.流量控制、消峰填谷

                对于瞬间来的大流量,系统需要在自身能力范围内尽可能多的处理请求,直接拒绝处理不了的请求,但是这样用户体验上不是很好,因为可以使用消息中间件的能力,既保护架构,又能够提升用户体验。

                4. 其他

                比如广播、事务型、最终一致性等特性,也是消息队列中常用到的功能。

2.使用消息队列带来的一些问题

              

        1.业务上增加响应延迟

       因为数据进入队列的原因,不可避免的会耽搁消费进度,导致业务生效不及时。

        2. 系统可用性降低: 系统可用性在某种程度上降低,为什么这样说呢?在加入MQ之前,你不用考虑消息丢失或 者说MQ挂掉等等的情况,但是,引入MQ之后你就需要去考虑了

        3. 系统复杂性提高: 加入MQ之后,你需要保证消息没有被重复消费(幂等)、处理消息丢失(可靠性)的情况、保证消息传递的顺序性、消息堆积、可靠性、延迟性、扩展性等等问题!

        4.一致性问题: 我上面讲了消息队列可以实现异步,消息队列带来的异步确实可以提高系统响应速度。但是,万 一消息的真正消费者并没有正确消费消息怎么办?这样就会导致数据不一致的情况了!

  3.JMS VS AMQP

        JMS(JAVA Message Service,java消息服务)是java的消息服务,JMS的客户端之间可以通过JMS服务进行异步的消息传输。JMS(JAVA Message Service,Java消息服务)API是一个消息服务的标准或者说是规范,允许应用程序组件基于JavaEE平台创建、发送、接收和读取消息。

       JMS两种消息模型:点到点和发布订阅,分别对应一对一和一对多;

       JMS定义了五种不同的消息正文格式,以及调用的消息类型,允许你发送并接收以一些不同形式的数据,提供现有消息格式的一些级别的兼容性。

  • StreamMessage -- Java原始值的数据流
  • MapMessage--一套名称-值对
  • TextMessage--一个字符串对象
  • ObjectMessage--一个序列化的 Java对象
  • BytesMessage--一个字节的数据流

        AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准 高级消息队列协议二进制应用层协议

对比方向JMSAMQP
定义Java API协议
跨语言
跨平台
支持消息类型提供两种消息模型:①Peer-2-Peer;②Pub/sub提供了五种消息模型:①direct exchange;②fanout exchange;③topic change;④headers exchange;⑤system exchange。本质来讲,后四种和JMS的pub/sub模型没有太大差别,仅是在路由机制上做了更详细的划分;
支持消息类型支持多种消息类型 ,我们在上面提到过byte[](二进制)

AMQP模型: 

总结:

  • AMQP 为消息定义了线路层(wire-level protocol)的协议,而JMS所定义的是API规范。在 Java 体系中,多个client均可以通过JMS进行交互,不需要应用修改代码,但是其对跨平台的支持较差。而AMQP天然具有跨平台、跨语言特性。
  • JMS 支持TextMessage、MapMessage 等复杂的消息类型;而 AMQP 仅支持 byte[] 消息类型(复杂的类型可序列化后发送)。
  • 由于Exchange 提供的路由算法,AMQP可以提供多样化的路由方式来传递消息到消息队列,而 JMS 仅支持 队列 和 主题/订阅 方式两种。
  4.常见的消息队列对比

常见的消息队列:

对比方向概要
吞吐量万级的 ActiveMQ 和 RabbitMQ 的吞吐量(ActiveMQ 的性能最差)要比 十万级甚至是百万级的 RocketMQKafka 低一个数量级。
可用性都可以实现高可用。ActiveMQ 和 RabbitMQ 都是基于主从架构实现高可用性。RocketMQ 基于分布式架构,kafka 也是,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
时效性RabbitMQ 基于erlang开发,所以并发能力很强,性能极其好,延时很低,达到微秒级。其他三个都是 ms 级。(语言特性
功能支持除了 Kafka,其他三个功能都较为完备。 Kafka 功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用,是事实上的标准
消息丢失ActiveMQ 和 RabbitMQ 丢失的可能性非常低, RocketMQ 和 Kafka 理论上不会丢失。
  5. 引入消息队列之后如何保证高可用性

RabbitMQ:镜像集群模式

        RabbitMQ 是基于主从做高可用性的,Rabbitmq有三种模式:单机模式、普通集群模式、镜像集群模式。单机模式一般在生产环境中很少用,普通集群模式只是提高了系统的吞吐量,让集群中多个节点来服务某个 Queue 的读写操作。那么真正实现 RabbitMQ 高可用的是镜像集群模式。镜像集群模式跟普通集群模式不一样的是,创建的 Queue,无论元数据还是Queue 里的消息都会存在于多个实例上,然后每次你写消息到 Queue 的时候,都会自动和多个实例的 Queue 进行消息同步。这样设计,好处在于:任何一个机器宕机不影响其他机器的使用。坏处在于:1. 性能开销太大:消息同步所有机器,导致网络带宽压力和消耗很重;2. 扩展性差:如果某个 Queue 负载很重,即便加机器,新增的机器也包含了这个 Queue 的所有数据,并没有办法线性扩展你的 Queue。 

补:RabbitMQ典型集群模式:主备、远程、镜像、多活

   主备:备用机平时不工作,有浪费;

   远程:配置比较复杂,麻烦;

   镜像:没有保证横向扩容;

   多活:借用Federation 插件方便的从上游拉取数据,扩展方便;

Kafka:partition 和 replica 机制

        Kafka 基本架构是多个 broker 组成,每个 broker 是一个节点。创建一个 topic 可以划分为多个 partition,每个 partition 可以存在于不同的 broker 上,每个 partition 就放一部分数据,这就是天然的分布式消息队列。就是说一个 topic 的数据,是分散放在多个机器上的,每个机器就放一部分数据。Kafka 0.8 以前,是没有 HA 机制的,任何一个 broker 宕机了,它的 partition 就没法写也没法读了,没有什么高可用性可言。Kafka 0.8 以后,提供了 HA 机制,就是 replica 副本机制。每个 partition 的数据都会同步到其他机器上,形成自己的多个 replica 副本。然后所有 replica 会选举一个 leader 出来,生产和消费都跟这个 leader 打交道,然后其他 replica 就是 follower。写的时候,leader 会负责把数据同步到所有 follower 上去,读的时候就直接读 leader 上数据即可。Kafka 会均匀的将一个 partition 的所有 replica 分布在不同的机器上,这样才可以提高容错性。 

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

    1.消息队列提供的幂等性机制(唯一主键id加指纹一般为时间戳)
  常见的消息队列如 Kafka、RocketMQ等提供了幂等性机制,能够确保同一条消息被消费多次时只会产生一次影响。在Kafka中,可以通过设置消息的key来实现幂等性。

  2.消费者自己维护消费记录
  消费者可以在消费一条消息后,将其在数据库中或者内存中记录下来。在消费下一条消息时,先查询是否已经消费过该消息,如果已经消费过,则不再处理。

  3.使用分布式锁
  在消费消息时,可以使用分布式锁来保证同一条消息只会被一个消费者处理。常见的分布式锁实现有 ZooKeeper、Redis 等。

  7. 如何保证消息的可靠性传输(如何处理消息丢失的问题)?

1.1.1 生产者没有成功把消息发送到MQ

a、丢失的原因:因为网络传输的不稳定性,当生产者在向MQ发送消息的过程中,MQ没有成功接收到消息,但是生产者却以为MQ成功接收到了消息,不会再次重复发送该消息,从而导致消息的丢失。

b、解决办法: 有两个解决办法:事务机制和confirm机制,最常用的是confirm机制。

事务机制:

      RabbitMQ 提供了事务功能,生产者发送数据之前开启 RabbitMQ 事务channel.txSelect,然后发送消息,如果消息没有成功被 RabbitMQ 接收到,那么生产者会收到异常报错,此时就可以回滚事务channel.txRollback,然后重试发送消息;如果收到了消息,那么可以提交事务channel.txCommit。

confirm机制:

      RabbitMQ可以开启 confirm 模式,在生产者那里设置开启 confirm 模式之后,生产者每次写的消息都会分配一个唯一的 id,如果消息成功写入 RabbitMQ 中,RabbitMQ 会给生产者回传一个 ack 消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你的一个 nack 接口,告诉你这个消息接收失败,生产者可以发送。而且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收到这个消息的回调,那么可以重发。

注意:RabbitMQ的事务机制是同步的,很耗型能,会降低RabbitMQ的吞吐量。confirm机制是异步的,生成者发送完一个消息之后,不需要等待RabbitMQ的回调,就可以发送下一个消息,当RabbitMQ成功接收到消息之后会自动异步的回调生产者的一个接口返回成功与否的消息。

1.1.2 RabbitMQ接收到消息之后丢失了消息

a、丢失的原因:RabbitMQ接收到生产者发送过来的消息,是存在内存中的,如果没有被消费完,此时RabbitMQ宕机了,那么再次启动的时候,原来内存中的那些消息都丢失了。

b、解决办法:开启RabbitMQ的持久化。当生产者把消息成功写入RabbitMQ之后,RabbitMQ就把消息持久化到磁盘。结合上面的说到的confirm机制,只有当消息成功持久化磁盘之后,才会回调生产者的接口返回ack消息,否则都算失败,生产者会重新发送。存入磁盘的消息不会丢失,就算RabbitMQ挂掉了,重启之后,他会读取磁盘中的消息,不会导致消息的丢失。

c、持久化的配置:

第一点是创建 queue 的时候将其设置为持久化,这样就可以保证 RabbitMQ 持久化 queue 的元数据,但是它是不会持久化 queue 里的数据的。

第二个是发送消息的时候将消息的 deliveryMode 设置为 2,就是将消息设置为持久化的,此时 RabbitMQ 就会将消息持久化到磁盘上去。

注意:持久化要起作用必须同时设置这两个持久化才行,RabbitMQ 哪怕是挂了,再次重启,也会从磁盘上重启恢复 queue,恢复这个 queue 里的数据。

1.1.3 消费者弄丢了消息

a、丢失的原因:如果RabbitMQ成功的把消息发送给了消费者,那么RabbitMQ的ack机制会自动的返回成功,表明发送消息成功,下次就不会发送这个消息。但如果就在此时,消费者还没处理完该消息,然后宕机了,那么这个消息就丢失了。

b、解决的办法:简单来说,就是必须关闭 RabbitMQ 的自动 ack,可以通过一个 api 来调用就行,然后每次在自己代码里确保处理完的时候,再在程序里 ack 一把。这样的话,如果你还没处理完,不就没有 ack了?那 RabbitMQ 就认为你还没处理完,这个时候 RabbitMQ 会把这个消费分配给别的 consumer 去处理,消息是不会丢的。

  8. 我该怎么保证从消息队列里拿到的数据按顺序执行?

     无法保证顺序性的原因:

   1.多个消费者时,每个消费者的消费速度不同,导致先消费的可能后落盘;

   2.单个消费者使用多线程消费可能导致,每个线程的消费速度不同,导致先消费的可能后落盘;

       

  9. 如何解决消息队列的延时以及过期失效问题?

假设使用的是 rabbitmq,rabbitmq是可以设置过期时间的(TTL),如果消息在 queue 中积压超过一定的时间就会被 rabbitmq 清理掉,数据就丢失了。

解决:手动写程序,将丢失的那批数据,一点点地查出来,然后重新插入到 mq 里面去。

10 . 消息队列满了以后该怎么处理?有几百万消息持续积压几小时, 说说怎么解决?

快速处理积压的消息
临时紧急扩容:具体操作步骤和思路如下:

1. 先修复 consumer 的问题,确保其恢复消费速度,

2. 然后将现有 consumer 都停掉新建一个 topic ,partition 是原来的 10 倍,临时建立好原先10倍或者20倍的 queue 数量,然后写一个临时的分发数据的 consumer 程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10被数量的 queue
接着临时征用10倍的机器来部署 consumer,每一批 consumer 消费一个临时 queue 的数据

 10. 如果让你来开发一个消息队列中间件,你会怎么设计架构?

参考:

1.为什么要使用消息队列_为什么要用消息队列_咸鱼豆芽菜的博客-CSDN博客

2.https://www.cnblogs.com/huojg-21442/articles/10873280.html

3.JMS VS AMQP_redfivehit的博客-CSDN博客

4.10月消息队列高频面试题详细解析 - 哔哩哔哩

5.Java Rabbitmq中四种集群架构的区别详解_java_AB教程网

6.消息队列专栏(七)如何保证消息可靠传递 - 知乎

7.如何保证消息的可靠性 - 简书

8.消息队列如何保证顺序性_低调的杉菜的博客-CSDN博客

9.https://www.cnblogs.com/cnndevelop/p/12091697.html


10.消息队列的延时以及过期失效问题_如何解决消息队列的延时以及过期失效问题?_smietao的博客-CSDN博客

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值