消息队列
消息队列是利用了数据结构中先进先出的一种数据结构——队列来实现的,在当前大部分企业的系统架构中,作为中间件提供服务。
消息中间件功能
应用解耦
AB应用不再互相依赖。
流量削峰
流量达到高峰的时候,通常使用限流算法来控制流量涌入系统,避免系统被击瘫,但是这种方式损失了一部分请求。
此时可以使用消息中间件来缓冲大量的请求,匀速消费,当消息队列中堆积消息过多时,我们可以动态上线增加消费端,来保证不丢失重要请求。
大数据处理
消息中间件可以把各个模块中产生的管理员操作日志、用户行为、系统状态等数据文件作为消息收集到主题中。
数据使用方可以订阅自己感兴趣的、互不影响的数据内容,进行消费。
异构系统
跨语言。
基本概念
1 RocketMQ 角色
消息模型(Message Model)
RocketMQ主要由 Producer、Broker、Consumer 三部分组成,其中Producer 负责生产消息,Consumer 负责消费消息,Broker 负责存储消息。Broker 在实际部署过程中对应一台服务器,每个 Broker 可以存储多个Topic的消息,每个Topic的消息也可以分片存储于不同的 Broker。Message Queue 用于存储消息的物理地址,每个Topic中的消息地址存储于多个 Message Queue 中。ConsumerGroup 由多个Consumer 实例构成。
Broker
Broker在RocketMQ系统中负责接收从生产者发送来的消息并存储、同时为消费者的拉取请求作准备。Broker也存储消息相关的元数据,包括消费者组、消费进度偏移和主题和队列消息等。
- Broker面向Producer和Consumer接收和发送消息
- 向nameserver提交自己的信息
- 是消息中间件的消息存储、转发服务器。
- 每个Broker节点,在启动时,都会遍历NameServer列表,与每个NameServer建立长连接,注册自己的信息,之后定时上报。
broker集群
- Broker高可用,可以配成Master/Slave结构,Master可写可读,Slave只可以读,Master将写入的数据同步给Slave。
- 一个Master可以对应多个Slave,但是一个Slave只能对应一个Master
- Master与Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave
- Master多机负载,可以部署多个broker
- 每个Broker与nameserver集群中的所有节点建立长连接,定时注册Topic信息到所有nameserver。
Producer
一个Producer会把业务应用系统里产生的消息发送到Broker服务器。RocketMQ提供多种发送方式,同步发送、异步发送、顺序发送、单向发送。同步和异步方式均需要Broker返回确认信息,单向发送不需要。
- 消息的生产者
- 与集群中的其中一个节点(随机选择)建立长连接,获得Topic的路由信息,包括Topic下面有哪些Queue,这些Queue分布在哪些Broker上等
- 接下来向提供Topic服务的Master建立长连接,且定时向Master发送心跳
生产者组(Producer Group)
同一类Producer的集合,这类Producer发送同一类消息且发送逻辑一致。如果发送的是事务消息且原始生产者在发送之后崩溃,则Broker服务器会联系同一生产者组的其他生产者实例以提交或回溯消费。
注意:考虑到提供的生产者在发送消息方面足够强大,因此每个生产者组仅允许拥有一个实例,以避免不必要的生产者实例初始化。
Consumer
消息的消费者,通过NameServer集群获得Topic的路由信息,连接到对应的Broker上消费消息。注意,由于Master和Slave都可以读取消息,因此Consumer会与Master和Slave都建立连接。从用户应用的角度而言提供了两种消费形式:拉取式消费、推动式消费。
- 拉取式消费(Pull Consumer):应用通常主动调用Consumer的拉消息方法从Broker服务器拉消息、主动权由应用控制。一旦获取了批量消息,应用就会启动消费过程。
- 推动式消费(Push Consumer):封装消息拉取,消费进度并在内部维护其他工作,并将回调接口留给最终用户来实现,该接口将在消息到达时执行。该模式下Broker收到数据后会主动推送给消费端,该消费模式一般实时性较高。
消费者组(Consumer Group)
同一类Consumer的集合,这类Consumer通常消费同一类消息且消费逻辑一致。消费者组使得在消息消费方面,实现负载均衡和容错的目标变得非常容易。要注意的是,消费者组的消费者实例必须订阅完全相同的Topic。RocketMQ 支持两种消息模式:集群消费(Clustering)和广播消费(Broadcasting)。
- 集群消费(Clustering):集群消费模式下,相同Consumer Group的每个Consumer实例平均分摊消息。
- 广播消费(Broadcasting):广播消费模式下,相同Consumer Group的每个Consumer实例都接收全量的消息。
NameServer
NameServer充当路由消息的提供者。生产者或消费者能够通过NameServer查找各主题相应的Broker IP列表。多个NameServer实例组成集群,但相互独立,没有信息交换,彼此是无状态的节点。底层由netty实现,提供了路由管理、服务注册、服务发现的功能。
- nameserver是服务发现者,集群中各个角色(producer、broker、consumer等)都需要定时向nameserver上报自己的状态,以便互相发现彼此,超时不上报的话,nameserver会把它从列表中剔除
- nameserver可以部署多个,当多个nameserver存在的时候,其他角色同时向他们上报信息,以保证高可用
- NameServer集群间互不通信,没有主备的概念
- nameserver内存式存储,nameserver中的broker、topic等信息默认不会持久化
- 为什么不用zookeeper?rocketmq为了提高性能,CAP定理,客户端负载均衡
2 RocketMQ消息模型
根据上述模型,我们可以更深入地探讨有关消息系统设计的一些主题:
- 消费者并发
- 消费者热点
- 消费者负载平衡
- 消息路由
- 连接复用
- 金丝雀部署
主题(Topic)
表示一类消息的集合,每个主题包含若干条消息,每条消息只能属于一个主题,是RocketMQ进行消息订阅的基本单位。主题是生产者传递消息和消费者拉取消息的类别。主题与生产者和消费者之间的关系非常松散。具体来说,一个主题可能有零个,一个或多个向其发送消息的生产者。相反,生产者可以发送不同主题的消息。从消费者的角度来看,一个主题可以由零个,一个或多个消费者组订阅。 与此类似,消费者组可以订阅一个或多个主题,只要该组的实例保持其订阅一致即可。
消息(Message)
消息系统所传输信息的物理载体,生产和消费数据的最小单位,每条消息必须属于一个主题,该主题可以理解为要发送给你的信件的地址。消息还可以具备可选的标签和额外的键值对。 例如,你可以为消息设置业务key,然后在broker上查找消息以在开发过程中诊断问题。RocketMQ中每个消息拥有唯一的Message ID,且可以携带具有业务标识的Key。系统提供了通过Message ID和Key查询消息的功能。
消息队列(Message Queue)
主题可以分为一个或多个子主题,即“message queue”。
标签(Tag)
为消息设置的标志,用于同一主题下区分不同类型的消息。来自同一业务单元的消息,可以根据不同业务目的在同一主题下设置不同标签。标签能够有效地保持代码的清晰度和连贯性,并优化RocketMQ提供的查询系统。消费者可以根据Tag实现对不同子主题的不同消费逻辑,实现更好的扩展性。
对比JSM中的Topic和Queue
在RocketMQ中Topic是一个逻辑上的概念,实际上Message是在每个Broker上以Queue的形式记录。
对应到JMS中的topic实现是由客户端来完成的。
3 消息顺序
使用DefaultMQPushConsumer时,你可以决定按顺序或并发地消费消息。
顺序消息
有序地消费消息意味着消息的消息顺序与生产者为每个消息队列发送消息的顺序相同。如果要处理必须强制执行全局顺序的情况,请确保你使用的主题只有一个消息队列。
注意:如果指定了顺序消费,则消息消费的最大并发度是消费者组订阅的消息队列数。
普通顺序消息(Normal Ordered Message)
普通顺序消费模式下,消费者通过同一个消费队列收到的消息是有顺序的,不同消息队列收到的消息则可能是无顺序的。
严格顺序消息(Strictly Ordered Message)
严格顺序消息模式下,消费者收到的所有消息均是有顺序的。
并发消息
当并发的消费消息时,消息消费的最大并发度仅受为每个消费者客户端指定的线程池限制。
注意:在此模式下不再保证消息顺序。