Contents
  1. 1. 什么是消息队列?
  2. 2. 为什么要使用消息队列?
    1. 2.0.1. 1.系统解耦,异步访问
    2. 2.0.2. 2.削弱高峰
    3. 2.0.3. 3.消息通信
  • 3. 消息模式
    1. 3.1. 1. 点对点模式和发布订阅模式:是否可以重复消费
    2. 3.2. 2. 推模式和拉模式:消息的更新者
  • 4. 消息队列的优缺点:
    1. 4.0.1. 1.系统可用性降低
    2. 4.0.2. 2.系统复杂性提高
  • 5. Kafka、ActiveMQ、RabbitMQ、RocketMQ对比:
  • 6. 如何保证消息队列的高可用:
  • 7. 如何保证消息不被重复消费:
  • 8. 如何保证消费的可靠性传输:
  • 9. 如何保证消息队列数据最终的一致性?
  • 10. 如何保证消息的顺序性?
  • 11. 消息队列实现的原理:
  • 12. JMS java message service
  • 参考文章 参考文章2

    什么是消息队列?

    消息队列就是在消息的传输过程中保存消息的容器。

    消息队列是分布式应用间交换信息的一种技术。

    img

    MQ的基本概念:

    1) 队列管理器(Queue Manage)

    队列管理器是MQ系统中最上层的一个概念,由它为我们提供基于队列的消息服务。

    2) 消息

    在MQ中,我们把应用程序交由MQ传输的数据定义为消息。

    3) 队列

    队列是消息的安全存放地,队列存储消息直到它被应用程序处理。

    4) 通道

    通道是两个管理器之间的一种单向点对点的通信连接,如果需要双向,需要建立一对通道。

    5) 监听器

    为什么要使用消息队列?

    1.系统解耦,异步访问

    看这么个场景。A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃……

    mq-1

    在这个场景中,A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。A 系统要时时刻刻考虑 BCDE 四个系统如果挂了该咋办?要不要重发,要不要把消息存起来?头发都白了啊!

    如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。

    mq-2

    场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口;

    传统模式的缺点:

    1) 假如库存系统无法访问,则订单减库存将失败,从而导致订单失败。

    2) 订单系统和库存系统耦合。

    而消息队列的出现解决了传统模式带来的弊端:

    1) 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。

    2)库存系统:订阅下单的消息,采用推拉的方式,获取下单信息,库存系统根据下单信息,进行库存操作。

    • 假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦。也减少用户请求的响应时间。
    2.削弱高峰

    流量削峰也是消息队列中常用场景,一般在秒杀或团抢活动中使用广泛。

    应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。

    传统处理模式:

    1. 可以控制活动的人数;
    2. 可以缓解短时间内高流量压垮应用;

      消息队列处理模式:

    3. 用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面;

    4. 秒杀业务根据消息队列中的请求信息,再做后续处理。

      传统模式的缺点:

      • 并发量大的时候,所有的请求直接怼到数据库,造成数据库连接异常

      中间件模式的的优点:

      • 系统A慢慢的按照数据库能处理的并发量,从消息队列中慢慢拉取消息。在生产中,这个短暂的高峰期积压是允许的。
    3.消息通信

    消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。

    消息队列的两种消息模式:点对点模式和分布订阅模式。 同步保证结果,异步保证效率

    消息模式

    1. 点对点模式和发布订阅模式:是否可以重复消费

    1.1 P2P模式:

    P2P模式包含三个角色:消息队列(Queue),发送者(Sender),接收者(Receiver)。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。
    P2P的特点

    1. 每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中)
    2. 发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列
    3. 接收者在成功接收消息之后需向队列应答成功

      如果希望发送的每个消息都会被成功处理的话,那么需要P2P模式。、

    1.2 Pub/sub模式:

    包含三个角色:主题(Topic),发布者Publisher),订阅者(Subscriber) 。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。
    Pub/Sub的特点

    1. 每个消息可以有多个消费者
    2. 发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息。
    3. 为了消费消息,订阅者必须保持运行的状态。

    为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。

    如果希望发送的消息可以不被做任何处理、或者只被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型。

    2. 推模式和拉模式:消息的更新者

    推(push)模式是一种基于C/S机制、由服务器主动将信息送到客户器的技术。

    1. 在push模式应用中,服务器把信息送给客户器之前,并没有明显的客户请求。push事务由服务器发起。push模式可以让信息主动、快速地寻找用户/客户器,信息的主动性和实时性**比较好。但精确性较差,可能推送的信息并不一定满足客户的需求。

    2. 推送模式不能保证能把信息送到客户器,因为推模式采用了广播机制,如果客户器正好联网并且和服务器在同一个频道上,推送模式才是有效的。

    3. push模式无法跟踪状态,采用了开环控制模式,没有用户反馈信息。在实际应用中,由客户器向服务器发送一个申请,并把自己的地址(如IP、port)告知服务器,然后服务器就源源不断地把信息推送到指定地址。在多媒体信息广播中也采用了推模式。

    拉(pull)模式与推模式相反,是由客户器主动发起的事务。

    消息队列的优缺点:

    优点可查看为什么使用MQ所述。

    缺点:

    1.系统可用性降低

    系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了,人 ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整,MQ 一挂,整套系统崩溃的,你不就完了?如何保证消息队列的高可用?

    2.系统复杂性提高

    硬生生加个 MQ 进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?数据一致性?怎么保证消息传递的顺序性?

    但是,我们该用还是要用的。

    Kafka、ActiveMQ、RabbitMQ、RocketMQ对比:

    (1)中小型软件公司,建议选RabbitMQ.一方面,erlang语言天生具备高并发的特性,而且他的管理界面用起来十分方便。正所谓,成也萧何,败也萧何!他的弊端也在这里,虽然RabbitMQ是开源的,然而国内有几个能定制化开发erlang的程序员呢?所幸,RabbitMQ的社区十分活跃,可以解决开发过程中遇到的bug,这点对于中小型公司来说十分重要。

    不考虑rocketmq和kafka的原因是,一方面中小型软件公司不如互联网公司,数据量没那么大,选消息中间件,应首选功能比较完备的,所以kafka排除。

    不考虑rocketmq的原因是,rocketmq是阿里出品,如果阿里放弃维护rocketmq,中小型公司一般抽不出人来进行rocketmq的定制化开发,因此不推荐。

    (2)大型软件公司,根据具体使用在rocketMq和kafka之间二选一。一方面,大型软件公司,具备足够的资金搭建分布式环境,也具备足够大的数据量。

    针对rocketMQ,大型软件公司也可以抽出人手对rocketMQ进行定制化开发,毕竟国内有能力改JAVA源码的人,还是相当多的。至于kafka,根据业务场景选择,如果有日志采集,大数据领域的实时计算等场景,肯定是首选kafka了,几乎是全世界这个领域的事实性规范。具体该选哪个,看使用场景。

    特性 ActiveMQ RabbitMQ RocketMQ Kafka
    单机吞吐量 万级,比 RocketMQ、Kafka 低一个数量级 同 ActiveMQ 10 万级,支撑高吞吐 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景
    topic 数量对吞吐量的影响 topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源
    时效性 ms 级 微秒级,这是 RabbitMQ 的一大特点,延迟最低 ms 级 延迟在 ms 级以内
    可用性 高,基于主从架构实现高可用 同 ActiveMQ 非常高,分布式架构 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
    消息可靠性 有较低的概率丢失数据 经过参数优化配置,可以做到 0 丢失 同 RocketMQ
    功能支持 MQ 领域的功能极其完备 基于 erlang 开发,并发能力很强,性能极好,延时很低 MQ 功能较为完善,还是分布式的,扩展性好 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用

    ###

    如何保证消息队列的高可用:

    分析:在第二点说过了,引入消息队列后,系统的可用性下降。在生产中,没人使用单机模式的消息队列。因此,作为一个合格的程序员,应该对消息队列的高可用有很深刻的了解。

    如果面试的时候,面试官问,你们的消息中间件如何保证高可用的?你的回答只是表明自己只会订阅和发布消息,面试官就会怀疑你是不是只是自己搭着玩,压根没在生产用过。请做一个爱思考,会思考,懂思考的程序员.

    回答:要对消息队列的集群模式有深刻的了解。

    RabbitMQ的集群模式:普通集群和镜像集群。

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

    ————————————- ——–未完待续—————————————————

    如何保证消息不被重复消费:

    分析:这个问题其实换一种问法就是,如何保证消息队列的幂等性?这个问题可以认为是消息队列领域的基本问题。换句话来说,是在考察你的设计能力,这个问题的回答可以根据具体的业务场景来答,没有固定的答案。

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

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

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

      (1)比如,你拿到这个消息做数据库的insert操作。那就容易了,给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
      (2)再比如,你拿到这个消息做redis的set的操作,那就容易了,不用解决,因为你无论set几次结果都是一样的,set操作本来就算幂等操作。
      (3)如果上面两种情况还不行,上大招。准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。

    补充:鉴别消息重复,并幂等处理重复消息:

    1.鉴别重复消息:利用存储系统的唯一键,给每个消息加上一个MessageId.

    2.幂等处理重复消息的方法:

    ​ 1.跟鉴别消息重复一样,利用messageId,重复即不处理。

    ​ 2.版本号,每个消息都带一个版本号,只处理比当前存储版本号高的消息

    ​ 3.状态机,跟业务耦合比较严重,根据业务类型判断。

    RabbitMQ的ACK消息确认机制:防止消息丢失,如果消息者领取消息后没执行操作就挂掉了,或者执行抛出异常,也就是消费者执行失败了,rabbotMQ无从得知,就会导致消息丢失。

    因此RabbitMQ中就有ACK消息确认机制,消息确认有两种模式:

    1.自动模式,我们无需任何操作,在消息被消费者领取后,就会自动确认,消息也会被从队列删除。

    2.手动模式,消息被消费后,我们需要调用RabbitMQ提供的API来实现消息确认。
    我们在调用:channel.basicConsume()方法的时候,通过指定第二个参数来设置是自动还是手动:

    如何保证消费的可靠性传输:

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

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

    RabbitMQ

    (1)生产者丢数据
    从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息。
    transaction机制就是说,发送消息前,开启事务(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事物就会回滚(channel.txRollback()),如果发送成功则提交事物(channel.txCommit())。

    然而缺点就是吞吐量下降了。因此,按照博主的经验,生产上用confirm模式的居多。一旦channel进入confirm模式,所有在该信道上面发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后,rabbitMQ就会发送一个Ack给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了.如果rabiitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。

    (2)消息队列丢数据
    处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。

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

    1、将queue的持久化标识durable设置为true,则代表是一个持久的队列
    2、发送消息的时候将deliveryMode=2

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

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

    如何保证消息队列数据最终的一致性?

    依靠消息凭证完成最终一致性。

    ——————————待完善——————-

    如何保证消息的顺序性?

    分析:其实并非所有的公司都有这种业务需求,但是还是对这个问题要有所复习。

    回答:针对这个问题,通过某种算法,将需要保持先后顺序的消息放到同一个消息队列中(kafka中就是partition,rabbitMq中就是queue)。然后只用一个消费者去消费该队列。

    有的人会问:那如果为了吞吐量,有多个消费者去消费怎么办?

    这个问题,没有固定回答的套路。比如我们有一个微博的操作,发微博、写评论、删除微博,这三个异步操作。如果是这样一个业务场景,那只要重试就行。比如你一个消费者先执行了写评论的操作,但是这时候,微博都还没发,写评论一定是失败的,等一段时间。等另一个消费者,先执行写评论的操作后,再执行,就可以成功。

    总之,针对这个问题,我的观点是保证入队有序就行,出队以后的顺序交给消费者自己去保证,没有固定套路。

    消息队列实现的原理:

    JMS java message service

    jms是java平台中一个面向消息中间件的技术规范,MQ可以基于JMS规范实现,也可以基于其他技术规范实现。

    Contents
    1. 1. 什么是消息队列?
    2. 2. 为什么要使用消息队列?
      1. 2.0.1. 1.系统解耦,异步访问
      2. 2.0.2. 2.削弱高峰
      3. 2.0.3. 3.消息通信
  • 3. 消息模式
    1. 3.1. 1. 点对点模式和发布订阅模式:是否可以重复消费
    2. 3.2. 2. 推模式和拉模式:消息的更新者
  • 4. 消息队列的优缺点:
    1. 4.0.1. 1.系统可用性降低
    2. 4.0.2. 2.系统复杂性提高
  • 5. Kafka、ActiveMQ、RabbitMQ、RocketMQ对比:
  • 6. 如何保证消息队列的高可用:
  • 7. 如何保证消息不被重复消费:
  • 8. 如何保证消费的可靠性传输:
  • 9. 如何保证消息队列数据最终的一致性?
  • 10. 如何保证消息的顺序性?
  • 11. 消息队列实现的原理:
  • 12. JMS java message service