什么是消息队列?
为什么要用消息队列?
即,应用场景是什么,也就是用了有什么好处
解耦
多应用间通过消息队列对同一消息进行处理,避免调用接口失败导致整个过程失败
异步
多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理,减少处理时间
削峰/限流
避免流量过大导致应用系统挂掉的情况
使用消息队列需要注意什么?
系统复杂性增加
如何保证消息队列是高可用,即做到集群高可用
如何保证消费的可靠性传输,即不丢消息
如何保证消息不被重复消费,即保证消费的幂等性
如何保证消息的顺序性,即保证数据的逻辑正确性
简单分析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
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。