浅谈消息队列之RocketMQ

什么是消息队列?

 

 
 

为什么要用消息队列?

   即,应用场景是什么,也就是用了有什么好处

    解耦

        多应用间通过消息队列对同一消息进行处理,避免调用接口失败导致整个过程失败

    异步

        多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理,减少处理时间

    削峰/限流

        避免流量过大导致应用系统挂掉的情况

 


使用消息队列需要注意什么?

系统复杂性增加

如何保证消息队列是高可用,即做到集群高可用

如何保证消费的可靠性传输,即不丢消息

如何保证消息不被重复消费,即保证消费的幂等性

如何保证消息的顺序性,即保证数据的逻辑正确性


简单分析RocketMQ的原理

高可用

上架构

 
 

NameServer

维持心跳和提供Topic-Broker的关系数据,多个Namesrv之间相互没有通信,单台Namesrv宕机不影响其他Namesrv与集群;即使整个Namesrv集群宕机,已经正常工作的Producer,Consumer,Broker仍然能正常工作,但新起的Producer, Consumer,Broker就无法工作,nameserver不会有频繁的读写,所以性能开销非常小,稳定性很高

Broker

Broker与Namesrv的心跳机制:单个Broker跟所有Namesrv保持心跳请求,心跳间隔为30秒,心跳请求中包括当前Broker所有的Topic信息

高可靠并发读写服务:所有发往broker的消息,有同步刷盘和异步刷盘机制,同步刷盘时,消息写入物理文件才会返回成功,因此非常可靠;异步刷盘时,只有机器宕机,才会产生消息丢失,broker挂掉可能会发生,但是机器宕机崩溃是很少发生的,除非突然断电。

负载均衡:Broker上存Topic信息,Topic由多个队列组成,队列会平均分散在多个Broker上,而Producer的发送机制保证消息尽量平均分布到所有队列中,最终效果就是所有消息都平均落在每个Broker上

高可用:集群部署时一般都为主备,Broker名相同的一组Master/Slave Broker,其中包含一个Master Broker(Broker Id为0)和0~N个Slave Broker(Broker Id不为0),备机实时从主机同步消息,如果其中一个主机宕机,备机提供消费服务,但不提供写服务。

Producer

Producer启动时,也需要指定Namesrv的地址,从Namesrv集群中选一台Master建立长连接,生产者每30秒从Namesrv获取Topic跟Broker的映射关系,更新到本地内存中。再跟Topic涉及的所有Broker建立长连接

生产者发送时,会自动轮询当前所有可发送的broker,一条消息发送成功,下次换另外一个broker发送,以达到消息平均落到所有的broker上。假如某个Broker宕机,意味生产者最长需要30秒才能感知到。在这期间会向宕机的Broker发送消息。当一条消息发送到某个Broker失败后,会往该broker自动再重发2次,假如还是发送失败,则抛出发送失败异常。业务捕获异常,重新发送即可。客户端里会自动轮询另外一个Broker重新发送,这个对于用户是透明的

消息发送方式分为,同步发送,异步发送,单向发送

Consumer

消费者启动时需要指定Namesrv地址,与其中一个Namesrv建立长连接。消费者每隔30秒从nameserver获取所有topic的最新队列情况

 Consumer跟Broker是长连接,会每隔30秒发心跳信息到Broker。Broker端每10秒检查一次当前存活的Consumer,若发现某个Consumer 2分钟内没有心跳,就断开与该Consumer的连接,并且向该消费    组的其他实例发送通知,触发该消费者集群的负载均衡。

消费者得到master宕机通知后,转向slave消费(重定向,对于2次开发者透明),但是slave不能保证master的消息100%都同步过来了,因此会有少量的消息丢失。但是消息最终不会丢的,一旦master恢复,未同步过去的消息会被消费掉。

消费分为集群消费和广播消费,


Topic+Queue :

topic的逻辑存储模型:

 
 

如果各Master Broker有Slave Broker,Slave Broker中的结构和其对应的Master Broker完全相同。

Topic是逻辑概念,对于RocketMQ,一个Topic可以分布在各个Broker上,把一个Topic分布在一个Broker上的子集定义为一个Topic分片,其实就是在某一broke上一个topic的部分数据

Queue 存在的意义:每个Topic分片等分的Queue的数量可以不同,由用户在创建Topic时指定, 是消费负载均衡过程中资源分配的基本单元.

Topic 的创建过程:

 
 

创建topic需要指定的参数,

    -b 指定broker上创建topic

    -c 指定cluster创建topic

-n 指定namesrv地址,cluster模式下必须从namesrv获取broker地址,支持cluster模式下创建topic和支持broker模式下创建topic

    -t topic的名字标志

    -r/w 读写队列的个数,建议相等

    -o 待研究不确定是不是保证全局有序消息的配置

 
 

存储持久化

消息队列的存储选型:

分布式KV存储,文件系统(目前业界较为常用的几款产品RocketMQ/Kafka/RabbitMQ 均采用的是消息刷盘至所部署虚拟机/物理机的文件系统来做持久化,关系性DB(ActiveMQ)

从高可靠,高效率,中间件减少对第三方的依赖考虑, 文件系统>分布式KV存储>关系型数据库DB

存储架构:

 
 

对比下Kafka的存储结构:

 
 

 每个Topic有多个partition(queue),kafka的每个partition都是一个独立的物理文件, 消息直接从里面读写

RocketMQ存储的特点:

1.Broker单个实例下所有的队列共用一个日志数据文件(即为CommitLog)来存储

2.consumerQueue 是个消费的逻辑队列,保存了数据在commit log中的offset

3. 消费读取数据,需要先读取consumerQueue,再读取commit log,消息主体都是通过CommitLog来进行读写.

缺点:

1. 顺序写,随即读

克服缺点:

由于Consume Queue存储数据量极少, 而且是顺序读, 在PAGECACHE预读作用下, Consume Queue的读性能几乎与内存一致, 即使堆积情况下. 所以可认为Consume Queue完全不会阻碍读性能

小结

RocketMQ可以严格的保证消息有序。但这个顺序,不是全局顺序,只是分区(queue)顺序。要全局顺序只能一个分区

RocketMQ不保证消息不重复,如果你的业务需要保证严格的不重复消息,需要你自己在业务端去重



作者:王洪伦
链接:https://www.jianshu.com/p/027accb2b7ae
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

posted on 2020-01-07 21:15  二云  阅读(1735)  评论(0编辑  收藏  举报

导航