美文网首页
消息队列

消息队列

作者: tdwmarlboro | 来源:发表于2018-05-25 14:53 被阅读0次

    https://www.cnblogs.com/charlesblc/p/6045238.html

    Java消息服务(Java Message Service JMS)应用程序接口是一个Java平台中关于面向消息中间件MOM的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。

    点对点与发布订阅最初是由JMS定义的。这两种模式主要区别或解决的问题就是发送到队列的消息是否重复消费

    1、定义

    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既支持内存队列也支持持久化队列,消费端为推模型,消费状态和订阅关系由服务端负责维护,消息消费完后立即删除,不保留历史消息。

    3.2、Kafka

    Kafka只支持消息持久化,消费端为拉模型,消费状态和订阅关系由客户端端负责维护,消息消费完后不会立即删除,会保留历史消息。

    ①Push方式:由消息中间件主动地将消息推送给消费者;

    ②Pull方式:由消费者主动向消息中间件拉取消息。

    采用Push方式,可以尽可能快地将消息发送给消费者(stream messages to consumersasfastas possible)

    而采用Pull方式,会增加消息的延迟,即消息到达消费者的时间有点长(adds significant latency per message)。

    如果消费者的处理消息的能力很弱(一条消息需要很长的时间处理),而消息中间件不断地向消费者Push消息,消费者的缓冲区可能会溢出。

    prefetch limit 规定了一次可以向消费者Push(推送)多少条消息。

    当推送消息的数量到达了perfetch limit规定的数值时,消费者还没有向消息中间件返回ACK,消息中间件将不再继续向消费者推送消息。

    如果消息的数量很少(生产者生产消息的速率不快),但是每条消息消费者需要很长的时间处理,那么prefetch limit设置为1比较合适。这样,消费者每次只会收到一条消息,当它处理完这条消息之后,向消息中间件发送ACK,此时消息中间件再向消费者推送下一条消息。

    prefetch limit设置为0意味着,消费者去轮询消息中间件获取消息,不再是Push方式了,而是Pull方式了。

    消费者两种方式获取消息:同步方式和异步方式。

    同步方式使用的是ActiveMQMessageConsumer的receive()方法。而异步方式则采用消费者实现MessageListener接口,监听消息。

    高级消息队列协议(AMQP)是一个异步消息传递所使用的应用层协议规范。作为线路层协议,而不是API(JMS),AMQP客户端能够无视消息的来源任意发送和接受信息。

    AMQP协议是一种二进制协议,提供客户端应用与消息中间件之间异步、安全、高效地交互。

    Push和Pull

    慢消费:

    慢消费无疑是push模型最大的致命伤,穿成流水线来看,如果消费者的速度比发送者的速度慢很多,势必造成消息在broker的堆积。假设这些消息都是有用的无法丢弃的,消息就要一直在broker端保存。当然这还不是最致命的,最致命的broker给consumer推送一堆consumer无法处理的消息,consumer不是reject就是error,然后来回踢皮球。

    反观pull模式,consumer可以按需消费,不用担心自己处理不了的消息来骚扰自己,而broker堆积消息也会相对简单,无需记录每一个要发送消息的状态,只需要维护所有消息的队列和偏移量就可以了。所以对于建立索引等慢消费,消息量有限且到来的速度不均匀的情况,pull模式比较合适。

    消息延迟与忙等

    这是pull模式最大的短板。由于主动权在消费方,消费方无法准确地决定何时去拉取最新的消息。如果一次pull取到消息了还可以继续去pull,如果没有pull取到则需要等待一段时间重新pull。

    RocketMq中有一种优化的做法-长轮询,基本思路是:消费者如果尝试拉取失败,不是直接return,而是把连接挂在那里wait,服务端如果有新的消息到来,把连接notify起来。

    顺序消息

    如果push模式的消息队列,支持分区,单分区只支持一个消费者消费,并且消费者只有确认一个消息消费后才能push送另一个消息,还要发送者保证全局顺序唯一,听起来也能做顺序消息,但是成本太高,尤其是必须每个消息消费确认后才能下一条消息。

    pull模式实现全局顺序消息:1、producer对应partition,并且单线程;2、consumer对应partition,消费确认(或批量确认),继续消费即可。

    相关文章

      网友评论

          本文标题:消息队列

          本文链接:https://www.haomeiwen.com/subject/upbefftx.html