RocketMq基础一

MQ选型对比

1、ActiveMQ

  • ActiveMQ采用消息推送方式,所以最适合的场景是默认消息都可在短时间内被消费。数据量越大,查找和消费消息就越慢,消息积压程度与消息速度成反比。
  • 吞吐量低。由于ActiveMQ需要建立索引,导致吞吐量下降。这是无法克服的缺点,只要使用完全符合JMS规范的消息中间件,就要接受这个级别的TPS。
  • 无分片功能。这是一个功能缺失,JMS并没有规定消息中间件的集群、分片机制。而由于ActiveMQ是为企业级开发设计的消息中间件,初衷并不是为了处理海量消息和高并发请求。如果一台服务器不能承受更多消息,则需要横向拆分

2、Kafka

  • ​ 性能较高,基本发送消息给kafka都是毫秒级性能【每秒十几万ops】,可用性很高,支持集群部署,部分宕机仍然可继续运行
  • 存在数据丢失问题,由于存储在磁盘缓冲区,没有录到物理磁盘,机器故障会导致数据丢失
  • 功能单一,主要支持发送消费消息,没有额外高级功能
  • 主要用在日志采集和传输上

3、RabbitMQ

  • ​ 保证数据不丢失,高可用性
  • 支持部分高级功能,比如死信队列,消息重试
  • 吞度量比较低,每秒几万级别,高并发情况,支撑困难,且集群扩展比较麻烦
  • 其语言是erlang,导致无法更改源代码
  • 适用于中小型企业

4、RocketMQ

  • 吞吐量高,可达10万qps以上,而且可保证高可用性,性能很高
  • 可通过配置保证数据不丢失,可部署大规模集群
  • 支持高级功能,延迟消息,事务消息,消息回溯,死信队列,消息积压等
  • java语言开发,可修改源码,阅读源码 ,唯一不足就是官方文档简单

rocket架构组成

整体的架构设计主要分为四大部分,分别是:Producer、Consumer、Broker、
NameServer。
在这里插入图片描述

1)Producer:就是消息生产者,可以集群部署。它会先和 NameServer 集群中的随机一台建立长连接,得知当前要发送的 Topic 存在哪台 Broker Master上,然后再与其建立长连接,支持多种负载平衡模式发送消息。
2)Consumer:消息消费者,也可以集群部署。它也会先和 NameServer 集群中的随机一台建立长连接,得知当前要消息的 Topic 存在哪台 Broker Master、Slave上,然后它们建立长连接,支持集群消费和广播消费消息。
3)Broker:主要负责消息的存储、查询消费,支持主从部署,一个 Master 可以对应多个 Slave,Master 支持读写,Slave 只支持读。Broker 会向集群中的每一台 NameServer 注册自己的路由信息。
4)NameServer:是一个很简单的 Topic 路由注册中心,支持 Broker 的动态注册和发现,保存 Topic 和 Borker 之间的关系。通常也是集群部署,但是各 NameServer 之间不会互相通信, 各 NameServer 都有完整的路由信息,即无状态。

rocketmq工作原理

在这里插入图片描述

nameServer核心工作原理

  • ​ nameServer集群部署【多台,防止单台宕机导致整个mq挂掉】,

  • ​ 每个broker启动时向所有的nameServer进行注册,

  • ​ 生产者消费者每隔一段时间主动去nameServer拉取broker信息

  • broker与nameserver如何通信?

 ①  采用tcp长连接进行通信,Broker会跟每个NameServer都建立一个TCP长连接,然后定时通过TCP长连接发送心跳请求过去
 ② broker与nameserver之间通过心跳机制确认broker是否宕机,broker会每个30秒向所有nameServer发送心跳告知nameserver自己仍然存活,每个nameser每个10s检查一次有没有哪个broker超过120s没发送心跳,如果有就说明broker党纪,从路由摘除broker

broker工作原理

  • ​ 主broker如何同步消息给从?主从模式采用pull模式,从broker不停的发送请求到主broker去拉取消息

  • rocketmq实现读写分离了么?主broker主要接受系统消息输入,然后同步给从broker,消费者的系统获取消息时,可能会从主或者从上获取

    主节点在返回消息到消息系统时,会根据主broker负责情况和从的同步情况,建议消息系统下次是从主还是从拉取数据

  • master 挂掉,从broker是否可以自动切换?不能,只能手动修改从节点配置,重新启动调整为master,而且中间一段时间不可用

  • mq4.5之后,dledger可以实现从节点切换为新的master【至少一个master,两个slaver,都会注册到nameserver】,三个broker组成一个group,保证一旦master党纪,可以从剩余的两个slave选举出一个新master对外提供服务

生产者发送broker原理

  • Topic作为一个数据集合是怎么在Broker集群里存储的 ?分布式存储
  • 生产者系统是如何将消息发送到Broker的呢?
消息发送前创建topic,发送消息时指定发送哪个topic,由于知道消息发送的topc,就可以与nameServer建立一个tcp长连接,定时从他那拉取最新的路由信息,包括及群里的哪些broker,哪些topic,每个topic存储在哪个broker上,生产者此时通过路由信息找到自己要投递的topic存在哪几台broker上,根据负载均很算法【轮询/hash】选择一台broker,选择后,就可以合对应的broker建立tcp长连接,即可通过长连接向broker发送消息

注:生产者一定是投递消息到master,master同步slave,实现备份,保证master故障数据不丢失,且slave可自动切换master提供服务

消费者拉取broker原理

原理同生产者类似,与nameserver建立长连接,从nameserver拉取路由数据,找到topic存储在哪个broker,与broker建立长连接,拉取消息
注:可从master/slave拉取消息

整体架构特点

高可用:nameserver集群化部署,保证路由信息完整
dledger技术实现slave自动切换maser,随便一台机器挂了没有太大影响
生产消费者集群化部署
高并发/海量消息:多个master部署方式,加上topic分散在多台broke中,可以抗下高并发和海量消息存储
可伸缩:集群中加入更多broker机器就可以实现线性扩展集群了

RocketMQ消息发送分类

RocketMQ 支持 3 种消息发送方式 :同 步(sync ) 、 异步(async)、单向(o neway )

同步发送消息
发送者向 MQ 执行发送消息 API 时,同步等待, 直到消息服务器返回发送结果 。

 @PostConstruct
    private void init() {
        log.info("======开始启动rocketmq producer=====");
        log.info("======nameserver地址:" + environment.getProperty("rocketmq.nameServer"));
        log.info("======groupName:" + groupName);
        sender = new DefaultMQProducer(groupName);
        sender.setNamesrvAddr(environment.getProperty("rocketmq.nameServer"));
        sender.setSendMsgTimeout(sendMsgTimeout);
        sender.setRetryTimesWhenSendFailed(retryTimesWhenSendFailed);

        try {
            //启动
            sender.start();
        } catch (MQClientException e) {
            log.error("====rocketmq producer启动失败======", e);
        }
        log.info("rocketmq producer启动成功");
    }
   
    private SendStatus setMsgBody(String message, String tag) {
        SendStatus status = null;
        if (sender != null) {
            Message msg = new Message();
            msg.setTags(tag);
            msg.setTopic(String.format(topic));
            try {
                //设置消息内容
                msg.setBody(message.getBytes(StandardCharsets.UTF_8));
                SendResult result = sender.send(msg);
                status = result.getSendStatus();
                log.info("消息发送完成,返回status:{}", status);
            } catch (Exception e) {
                log.error(groupName + "producer消息发送失败 :", e);
            }
        }
        return status;
    }

异步 发送
发送者向 MQ 执行发送消息 API 时,指定消息发送成功后的回掉函数,然后调用消息发送 API 后,立即返回,消息发送者线程不阻塞 ,直到运行结束,消息发送成功或失败的回调任务在一个新的线程中执行 。

	//设置异步发送失败次数时候重试次数为0
			defaultMQProducer.setRetryTimesWhenSendAsyncFailed(0);
			 
			defaultMQProducer.send(sendMsg,new SendCallback() {

				@Override
				public void onSuccess(SendResult sendResult) {
 
				}

				@Override
				public void onException(Throwable e) {
 					}
			});

单向发送
消息发送者向 MQ 执行发送消息 API 时,直接返回,不等待消息服务器的结果,也不注册回调函数,简单地说,就是只管发,不在乎消息是否成功存储在消息服务器上。

	defaultMQProducer.sendOneway(msg);

RocketMQ消息消费分类

对于任何⼀款消息中间件⽽⾔,消费者客户端⼀般有两种⽅式从消息中间件获取消息并消费:
(1)Push⽅式:由消息中间件主动地将消息推送给消费者;采⽤Push⽅式,可以尽可能实时地将消息发送给消费者进⾏消费。但是,在消费者的处理消息的能⼒较弱的时候(⽐如,消费者端的业务系统处理⼀条消息的流程⽐较复杂,其中的调⽤链路
⽐较多导致消费时间⽐较久。概括起来地说就是“慢消费问题”),⽽MQ不断地向消费者Push消息,消费者端的缓冲区可能会溢出,导致异常;

  
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(groupName);
      

2) Pull⽅式:由消费者客户端主动向消息中间件拉取消息;采⽤Pull⽅式,如何设置Pull消息的频率需要重点去考虑,举个例⼦来说,可能1分钟内连续来了1000条消息,然后2⼩时内没有新消息产⽣(概括起来说就是“消息延迟与忙等待”)。如果每次Pull的时间间隔⽐较久,会增加消息的延迟,即消息到达消费者的时间加长,MQ中消息的堆积量变⼤;若每次Pull的时间间隔较短,但是在⼀段时间内MQ中并没有任何消息可以消费,那么会产⽣很多⽆效的Pull请求的RPC开销,影响MQ整体的⽹络性能;

  DefaultMQPullConsumer consumer = new DefaultMQPullhConsumer(groupName);

3) 集群模式:默认情况就是集群模式,一个消费组获取到一条消息,只会交给组内的一台机器去处理,不是每台机器都可以获取到
这条消息的。

4)广播模式:consumer.setMessageModel(MessageModel.BROADCASTING);改为广播模式,那么对于消费组获取到的一条消息,组内每台机器都可以获取到这条消息。 【相对用的少】

Rocket底层原理

1、Topic、MessageQueue以及Broker之间到底是什么关系?

每个topic数据分布式存储在多个broker,如何确定能个topic分布在哪个broker,此刻引入messagequeue概念,本质上是数据分片机制,通过messagequeue将一个topic数据拆分成多个数据分片,然后每个broker机器上存储一些messagequeue,这样实现了topic的分布式存储
在这里插入图片描述

2、生产者发送消息的时候写入哪个MessageQueue

生产者从nameserver拉取topic元数据,通过topic就可以知道topic有哪几个messagequeque,每个messagequeque在哪台数据,这样就可以将数据存入到各自的brokerr对应的messagequeque

3、如果某个Broker出现故障该怎么办?

Producer中开启一个开关,就是sendLatencyFaultEnable,就可以避免一个Broker故障之后,短时间内生产者频繁的发送消息到这个故障的Broker上去,出现较多次数的异常

4、看看Broker对于接收到的消息,到底是如何存储到磁盘上去的?

1) 消息直接写入磁盘上的一个日志文件,叫做CommitLog【每个文件想定1GB,超出1Gb,会创建新的commitLog】,其实他同时会将这条消息在CommitLog中的物理位置,也就是一个文件偏移量,就是一个offset,写入到这条消息所属的MessageQueue对应的ConsumeQueue文件中去。  
2)对Topic下的每个MessageQueue都会有一系列的ConsumeQueue文件 ,就是broker的磁盘上格式为:【$HOME/store/consumequeue/{topic}/{queueId}/{fileName}】

实际上在ConsumeQueue中存储的每条数据不只是消息在CommitLog中的offset偏移量,还包含了消息的长度,以及tag
hashcode,一条数据是20个字节,每个ConsumeQueue文件保存30万条数据,大概每个文件是5.72MB。
在这里插入图片描述
注: 一个Broker机器而言,存储在他上面的所有Topic以及MessageQueue的消息数据都是写入一个统一的CommitLog的

5、如何让消息写入CommitLog文件近乎内存写性能的?

Broker是基于OS操作系统的PageCache和顺序写两个机制,来提升写入CommitLog文件的性能的。
1)broker以顺序方式将消息写入comitLog磁盘文件,比文件随机写性能提升很多
2) 数据写入commitlog时,先进入os的pageCache内存缓冲中 ,然后有os的后台选择时间异步化将os pageCache内从缓冲中数据刷入底层磁盘文件【pagecache写入+os异步刷盘策略】,
在这里插入图片描述
注意,broker写如pagecache就返回ack确认消息给生产者,而此刻broker宕机,数据还未写入磁盘,则存在数据丢失问题

6 同步刷盘

生产这发送消息,broker收到消息,强制消息刷入底层物理磁盘,才返回ack给producer,此时消息写入成功,如果broker还没来得及写入,就宕机,由于生产者并未烧到ack消息,所以可以感知消息发送失败,只要重试发送既可以了,保证了数据不丢失
注:同步步刷盘和异步刷盘各自的优缺点:高吞吐写入+丢失数据风险,写入吞吐量下降+数据不丢失
调整broker的配置文件,将其中的flushDiskType配置设置
为:SYNC_FLUSH,默认他的值是ASYNC_FLUSH,即默认是异步刷盘的。

7 基于DLedger技术部署的Broker高可用集群,到底如何进行数据同步的?

DLedger:有自己的commitlog机制,数据交给他,会写入commitlog磁盘文件,dledeger技术实现高可用架构实际上就是用DLedger先替换掉原来Broker自己管理的CommitLog,由DLedger来管理CommitLog

DLedger是基于Raft协议来互相投票进行Leader Broker选举,只要有(n台机器 / 2) + 1个人投票给某个人,就会选举他当Leader

Dledger采用Raft协议进行多副本同步,数据同步分为uncommitted和commited两个阶段,首先leader上的dleger收到一条消息,标记状态为uncommiteed,通过dleger组件把uncommited状态数据同步给fllower deledger服务,fllower deledger服务收到消息后,返回ack给leader,如果leder超过半数以上就将状态标记为committed,然后leader发送给fllower,flower将消息标记为commiteed
在这里插入图片描述
注意:如果leaderbroker宕机,可以基于Dledger技术和raft协议重新选举leader

8 消费者是如何从Broker拉取消息回来,进行处理以及ACK的?如果消费者故障了会如何处理

1)根据要消费的MessageQueue以及开始消费的位置,去找到对应的ConsumeQueue读取里面对
应位置的消息在CommitLog中的物理offset偏移量,然后到CommitLog中根据offset读取消息数据,返回给消费者机器。
2)消费者机器拉取到一批消息之后,就会将这批消息回调我们注册的一个函数

 @Autowired
 private MessageListenerConcurrently mqMessageListenerProcessor;
 consumer.registerMessageListener(mqMessageListenerProcessor);
public class MQConsumeMsgListenerProcessor implements MessageListenerConcurrently {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
//处理消息
//标记该消息已经成功被消费
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}}

3)消息处理完,消费者机器会提交消费进度到broker,broker会存储我们的消费进度,标记ConsumeOffset,下次该消费组就可以继续从这个位置继续拉取
4)出现宕机,则重新给各个消费机器分配他们要处理的MessageQueue。
一个 MessageQueue只能被一个消费机器去处理,但是一台消费者机器可以负责多个MessageQueue的消息处理

9 消费者到底是基于什么策略选择Master或Slave拉取数据的?

1)ConsumeQueue文件读取:基于os cache读取,在消费者机器拉取消息的时候,第一步大量的频繁读取ConsumeQueue文件,几乎可以说就是跟读内存里的数据的性能是一样的,通过这个就可以保证数据消费的高性能以及高吞吐
2)CommitLog文件读取:基于os cache+磁盘一起读取

  • 如果读取刚写入的commitlog数据,大概率时从os cache读取的commitlog数据,由于操作内存性能高
  • 如果读取比较早写入的commitlog数据,则是从磁盘读取,性能比较差些
    3)对比你当前没有拉取消息的数量和大小,以及最多可以存放在os cache内存里的消息的大小,如
    果你没拉取的消息超过了最大能使用的内存的量,那么说明你后续会频繁从磁盘加载数据,此时就让你从slave broker去加载数据了!
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值