消息队列支持高并发,高吞吐量的原因 ?
- 顺序读写
- 零拷贝 正是因为分了partion,才方便进行零拷贝。
- 文件分段:操作小文件肯定比大文件要方便。
- 批量发送
- 数据压缩
消费者 与 消息存储方Broker一般有两种通信机制:推(PUSH)、拉(PULL)
推模式:消息发送者将消息发送到Broker,然后Broker主动推送给订阅了该消息的消费者。
拉模式:消息发送者将消息发送到Broker上,然后由消息消费者自发的向Broker拉取消息。
推模式
优点当然是快。一般是采取客户端和服务器端建立长连接来实现的。
原来的一个缺点是客户端机器多,所以server端的长连接的数量太多。但是netty等NIO框架的出现让这些都不是事。
另一个问题是抗堆积性不好。server需要对堆积的大量数据发起大量随机的读写。无论是关系型数据库,还是采用LSM架构的HBASE(主要是随机读的问题),都难以胜任。
拉模式
缺点当然是慢。
优点是采用顺序读写,所以文件就可以了。
Producer
消息生产者,位于用户的进程内,Producer通过NameServer获取所有Broker的路由信息
,根据负载均衡策略选择将消息发到哪个Broker,然后调用Broker接口提交消息。
Producer Group
生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者组。
Consumer
消息消费者,位于用户进程内。Consumer通过NameServer获取所有broker的路由信息后,向Broker发送Pull请求来获取消息数据。Consumer可以以两种模式启动,广播(Broadcast)和集群(Cluster),广播模式下,一条消息会发送给所有Consumer,集群模式下消息只会发送给一个Consumer。
Consumer Group
消费者组,和生产者类似,消费同一类消息的多个 Consumer 实例组成一个消费者组。
Topic
Topic用于将消息按主题做划分,Producer将消息发往指定的Topic,Consumer订阅该Topic就可以收到这条消息。Topic跟发送方和消费方都没有强关联关系,发送方可以同时往多个Topic投放消息,消费方也可以订阅多个Topic的消息。在RocketMQ中,Topic是一个上逻辑概念。消息存储不会按Topic分开。
Message
代表一条消息,使用MessageId
唯一识别,用户在发送时可以设置messageKey,便于之后查询和跟踪。一个 Message 必须指定 Topic,相当于寄信的地址。Message 还有一个可选的 Tag 设置,以便消费端可以基于 Tag 进行过滤消息。也可以添加额外的键值对,例如你需要一个业务 key 来查找 Broker 上的消息,方便在开发过程中诊断问题。
Tag
标签可以被认为是对 Topic 进一步细化。一般在相同业务模块中通过引入标签来标记不同用途的消息。
Broker
Broker是RocketMQ的核心模块,负责接收并存储消息
,同时提供Push/Pull接口来将消息发送给Consumer。Consumer可选择从Master或者Slave读取数据。多个主/从组成Broker集群,集群内的Master节点之间不做数据交互。Broker同时提供消息查询的功能,可以通过MessageID和MessageKey来查询消息。Borker会将自己的Topic配置信息实时同步到NameServer。
Queue
Topic和Queue是1对多的关系,一个Topic下可以包含多个Queue,主要用于负载均衡。发送消息时,用户只指定Topic,Producer会根据Topic的路由信息选择具体发到哪个Queue上。Consumer订阅消息时,会根据负载均衡策略决定订阅哪些Queue的消息。
Offset
RocketMQ在存储消息时会为每个Topic下的每个Queue生成一个消息的索引文件,每个Queue都对应一个Offset记录当前Queue中消息条数。
NameServer
NameServer可以看作是RocketMQ的注册中心,它管理两部分数据:集群的Topic-Queue的路由配置;Broker的实时配置信息。其它模块通过Nameserv提供的接口获取最新的Topic配置和路由信息。
Producer/Consumer
:通过查询接口获取Topic对应的Broker的地址信息Broker
: 注册配置信息到NameServer, 实时更新Topic信息到NameServer
详解Broker
1、Broker与Name Server关系
1)连接 单个Broker和所有Name Server保持长连接。
2)心跳
心跳间隔:每隔30秒向所有NameServer发送心跳,心跳包含了自身的Topic配置信息。
心跳超时:NameServer每隔10秒,扫描所有还存活的Broker连接,若某个连接2分钟内没有发送心跳数据,则断开连接。
3)断开:当Broker挂掉;NameServer会根据心跳超时主动关闭连接,一旦连接断开,会更新Topic与队列的对应关系,但不会通知生产者和消费者。
2、 负载均衡
一个Topic分布在多个Broker上,一个Broker可以配置多个Topic,它们是多对多的关系。
如果某个Topic消息量很大,应该给它多配置几个Queue,并且尽量多分布在不同Broker上,减轻某个Broker的压力。
3 、可用性
由于消息分布在各个Broker上,一旦某个Broker宕机,则该Broker上的消息读写都会受到影响。
所以RocketMQ提供了Master/Slave的结构,Salve定时从Master同步数据,如果Master宕机,则Slave提供消费服务,但是不能写入消息,此过程对应用透明,由RocketMQ内部解决。
有两个关键点:思考1
一旦某个broker master宕机,生产者和消费者多久才能发现?
受限于Rocketmq的网络连接机制,默认情况下最多需要30秒,因为消费者每隔30秒从nameserver获取所有topic的最新队列情况,这意味着某个broker如果宕机,客户端最多要30秒才能感知。
思考2
master恢复恢复后,消息能否恢复。
消费者得到Master宕机通知后,转向Slave消费,但是Slave不能保证Master的消息100%都同步过来了,因此会有少量的消息丢失。但是消息最终不会丢的,一旦Master恢复,未同步过去的消息会被消费掉。
Consumer (消费者)
1 、Consumer与Name Server关系
1)连接 : 单个Consumer和一台NameServer保持长连接,如果该NameServer挂掉,消费者会自动连接下一个NameServer,直到有可用连接为止,并能自动重连。
2)心跳: 与NameServer没有心跳
3)轮询时间 : 默认情况下,消费者每隔30秒从NameServer获取所有Topic的最新队列情况,这意味着某个Broker如果宕机,客户端最多要30秒才能感知。
2、 Consumer与Broker关系
1)连接 :单个消费者和该消费者关联的所有broker保持长连接。
3、 负载均衡
集群消费模式下,一个消费者集群多台机器共同消费一个Topic的多个队列,一个队列只会被一个消费者消费。如果某个消费者挂掉,分组内其它消费者会接替挂掉的消费者继续消费。
Producer(生产者)
1、 Producer与Name Server关系
1)连接 单个Producer和一台NameServer保持长连接,如果该NameServer挂掉,生产者会自动连接下一个NameServer,直到有可用连接为止,并能自动重连。
2)轮询时间 默认情况下,生产者每隔30秒从NameServer获取所有Topic的最新队列情况,这意味着某个Broker如果宕机,生产者最多要30秒才能感知,在此期间,
发往该broker的消息发送失败。
3)心跳 与nameserver没有心跳
2、 与broker关系
连接 单个生产者和该生产者关联的所有broker保持长连接。
如何保证消息不丢失?
一、producer重试发送消息
-
默认情况下,可以通过同步的方式阻塞式的发送,check SendStatus,状态是OK,表示消息一定成功的投递到了Broker,状态超时或者失败,则会触发默认的2次重试。此方法的发送结果,可能Broker存储成功了,也可能没成功
-
采取事务消息的投递方式,并不能保证消息100%投递成功到了Broker,但是如果消息发送Ack失败的话,此消息会存储在CommitLog当中,但是对ConsumerQueue是不可见的。可以在日志中查看到这条异常的消息,严格意义上来讲,也并没有完全丢失
-
RocketMQ支持 日志的索引,如果一条消息发送之后超时,也可以通过查询日志的API,来check是否在Broker存储成功
- 消息支持持久化到Commitlog里面,即使宕机后重启,未消费的消息也是可以加载出来的
2.Broker自身支持同步刷盘、异步刷盘的策略,可以保证接收到的消息一定存储在本地的内存中
3. Broker集群支持 1主N从的策略,支持同步复制和异步复制的方式,同步复制可以保证即使Master 磁盘崩溃,消息仍然不会丢失
消费者可以根据自身的策略批量Pull消息
-
Consumer自身维护一个持久化的offset(对应MessageQueue里面的min offset),标记已经成功消费或者已经成功发回到broker的消息下标
-
如果Consumer消费失败,那么它会把这个消息发回给Broker,发回成功后,再更新自己的offset
-
如果Consumer消费失败,发回给broker时,broker挂掉了,那么Consumer会定时重试这个操作
如果Consumer和broker一起挂了,消息也不会丢失,因为consumer 里面的offset是定时持久化的,重启之后,继续拉取offset之前的消息到本地