一、MQ应用场景
-
业务解耦
解耦是消息队列要解决的最本质问题。所谓解耦,简单点讲就是一个事务,只关心核心的流程。而需要依赖其他系统但不那么重要的事情,有通知即可,无需等待结果。换句话说,基于消息的模型,关心的是“通知”,而非“处理”。 -
最终一致性
最终一致性指的是两个系统的状态保持一致,要么都成功,要么都失败 -
广播
所有订阅了消息的接收者都能收到消息 -
错峰流控
上下游对于事情的处理能力是不同的,通过消息可以平缓事件处理速率
二、常见MQ比较
MQ对比.png- ActiveMQ:JMS规范、P2P、pub/sub
- RabbitMQ:AMQP规范、Exchange、绑定
- Kafka:超高吞吐、异步刷盘、可能丢消息
- RocketMQ:高性能、顺序消息、事务消息、海量堆积
三、RocketMQ架构
-
生产者集群
- 通过负载均衡,向Broker集群发送消息
- 发送过程中支持快速衰竭和低延迟
- 拥有相同的producerGroup,无状态
-
消费者集群
- 拥有相同的consumerGroup,Share Nothing
- 消费者分为PushConsumer和PullConsumer两种
- PushConsumer使用长轮询拉消息,可以制定间隔,实时性高,使用最多的方式。
- PullConsumer需要自己控制拉取的时机、怎么消费、流控、异常处理等等。主动权在你自己,但实现复杂。
- 支持集群消费和广播消费
- 集群消费:消息会投递给Consumer Group下的唯一一个consumer。消费进度保存在broker 上并持久化。
该模式下会保存消费进度,并定期更新到broker,本地不存储,重启时到broker上获取最新消费进度,相当于无状态,可以集群部署。 - 广播消费:消息会投递给Consumer Group 下的所有consumer。消费进度保存在本地。
该模式下,每个consumer消费进度保存到本地文件系统,启动时从本地加载消费进度,但是各个consumer之间消费进度互不影响,相当于share nothing,故也可以扩展,但是不同的consumer可能会重复消费。
- 集群消费:消息会投递给Consumer Group下的唯一一个consumer。消费进度保存在broker 上并持久化。
-
Broker集群
- 支持单master、双maste、主从。支持同步/异步刷盘、同步/异步复制。连接所有的broker
- 每个broker保存topic的部分信息,彼此之间无共享,可以通过部署多台broker来分担负载。
- broker扩容通过name server来实现,新加入的broker会把自己注册到name server,这样producer和consumer就可以从name server获取到最新的broker信息
- 集群方案:单master方案有单点;多master当某个master宕机会对写没有影响,但是该master上的消息在恢复前不能读,影响消息实时性;多master多slave方式,异步复制下会有丢消息可能,同步复制下不会丢消息但性能略低。
-
Name Server集群
- 在内存中维护topic的路由信息,Broker会定时的发送路由信息到NameServer中的每个机器进行更新
- 各个name server之间没有通信,一个宕机不影响其他,可以集群部署。
- Name server的扩容可以通过单独的http服务器来维护所有namesrv地址列表,让producer和consumer以及broker集群动态感知最新的namesrc上下线。但是这样增加了系统的复杂度和运维负担。
- 个人认为一个更好的方案是通过zk来动态扩容(类似于dubbo的服务发现)
- NameServer的集群是无状态的
四、常见概念
-
Commit log
实际存储消息的结构,持久化到磁盘文件。顺序写,随机读。
Commit log里面保存了不同topic的信息。文件大小1G,超过会创建新的文件。同一个机器的所有broker公用commit log -
Message Queue
存储消息的偏移量。读消息先读message queue,根据偏移量到commit log读消息本身。 -
Topic
一类消息的标识。比如order表示订单消息。 -
Tag
对topic进行进一步细分,用来过滤消息。比如createOrder表示订单创建,payOrder表示订单支付。 -
重试机制
消费发送失败会重试,默认3次。 -
oneway方式发送
消息通过网络发送出去就直接返回,不需要考虑消息是否保存成功,可以大幅提高吞吐量 -
消息的key
消息的key一般用来唯一标识一条消息,比如创建订单成功后消息,可以就可以取订单id。broker会根据key给消息创建索引,可以通过key快速查到消息。对于线上查问题比较方便。 -
消费进度更新问题
如果一次拉取了10条消息,但是在消费到第10条的时候宕机,那么更新的offset实际上还是第一条消息的offset,导致下次重启时会从第二条消息开始重新消费一次。这个问题目前rocketmq没有提供可靠的方案,只能自己做排重或者幂等处理。 -
消费失败消息的处理
如果消费失败或者抛异常,consumer会把这条消息发回broker,进入重试队列,然后broker会定时轮询重试队列,重新投递。如果消息一直消费失败,会被放到DLQ死信队列,这种情况只能靠人工干预了。 -
启动的时候从哪里消费
- 第一次启动会根据设置的消费策略来消费,有三种消费策略分别是:
- CONSUME_FROM_FIRST_OFFSET
- CONSUME_FROM_LAST_OFFSET
- CONSUME_FROM_TIMESTAMP
- 非第一次启动则会从上次保存的消费进度开始消费
- 第一次启动会根据设置的消费策略来消费,有三种消费策略分别是:
-
索引队列用来存储消息的索引key
-
使用mmap方式减少内存拷贝,提高读取性能。具体实现:FileChannel.map(RandomAccessFile)
RocketMQ123.png
五、模块工作
-
NameServer和Broker
-
NameServer主要做两件事情:
- 管理Broker,接受来自Broker集群发送的注册,以及提供心跳机制来检查我们的Broker是否还存活。
- 管理路由信息,每一个NameServer都存储有路由信息和队列信息,提供给Producer和Consumer查询。
-
Broker主要负责消息的存储和传递,消息查询等。主要包含远程模块、客户端管理、存储服务、HA服务和索引服务:
- Broker(不管是Master还是Slave)和每一台NameServer机器来建立TCP连接。
- Broker在启动的时候调用BrokerController中start方法,获取远程nameServerAddressList(远程NameServer服务列表)。
- Broker对nameServerAddressList进行for循环处理,注册自己配置的topic信息到NameServer集群的每一台机器中。
即每一台NameServer都有该Broker的topic的配置信息。Master与Master之间无连接,Master与Slave之间有连接。
-
-
Producer和NameServer
每一个Producer与NameServer集群中的一台机器建立TCP连接,从这台NameServer上拉取路由信息。 -
Producer和broker
Producer和它要发送的topic相关的Master类型的Broker建立TCP连接,用于发送消息以及定时的心跳信息。Broker中记录该Producer的信息,供查询使用。 -
Consumer和NameServer
每一个Consumer会和NameServer集群中的一台机器建立TCP连接,会从这台NameServer上拉取路由信息,进行负载均衡 -
Consumer和Broker
Consumer可以与Master或者Slave的Broker建立TCP连接来进行消费消息,Consumer也会向它所消费的Broker发送心跳信息,供Broker记录。
六、几种消息
1. 普通消息
2. 顺序消息
3. 延迟消息
4. 事务消息
网友评论