前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >RocketMQ 是如何发送消息

RocketMQ 是如何发送消息

作者头像
云扬四海
发布2022-11-30 15:36:54
9920
发布2022-11-30 15:36:54
举报
文章被收录于专栏:云扬四海

创建Topic的时候为何要指定MessageQueue数量?

  • 简单来说,就是你要指定你的这个Topic对应了多少个队列,也就是多少个MessageQueue。
  • MessageQueue就是RocketMQ中非常关键的一个数据分片机制,他通过MessageQueue将一个Topic的数据拆分为了很多个数据分片,然后在每个Broker机器上都存储一些MessageQueue。
  • Topic是一个逻辑上的概念,实际上在每个broker上以queue的形式保存,也就是说每个topic在broker上会划分成几个逻辑队列,每个逻辑队列保存一部分消息数据,但是保存的消息数据实际上不是真正的消息数据,而是指向commit log的消息索引。

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

  • 生产者会跟NameServer进行通信获取Topic的路由数据, 以生产者从NameServer中就会知道,一个Topic有几个MessageQueue,哪些MessageQueue在哪台Broker机器上
  • 让一个Topic中的数据分散在多个MessageQueue中,进而分散在多个Broker机器上,实现RocketMQ集群分布式存储海量的消息数据了

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

  • 如果某个Broker临时出现故障了,比如Master Broker挂了,此时正在等待的其他Slave Broker自动热切换为Master Broker,那么这个时候对这一组Broker就没有Master Broker可以写入了
  • 如果你还是按照之前的策略来均匀把数据写入各个Broker上的MessageQueue,那么会导致你在一段时间内,每次访问到这个挂掉的Master Broker都会访问失败,这个似乎不是我们想要的样子。
  • 对于这个问题,通常来说建议大家在Producer中开启一个开关,就是sendLatencyFaultEnable
  • 一旦打开了这个开关,那么他会有一个自动容错机制,比如如果某次访问一个Broker发现网络延迟有500ms,然后还无法访问,那么就会自动回避访问这个Broker一段时间,比如接下来3000ms内,就不会访问这个Broker了

RocketMQ 是如何持久化消息的

1、为什么Broker数据存储是最重要的一个环节

  • roker数据存储实际上才是一个MQ最核心的环节,他决定了生产者消息写入的吞吐量,决定了消息不能丢失,决定了消费者获取消息的吞吐量,这些都是由他决定的

2、CommitLog消息顺序写入机制

  • 当生产者的消息发送到一个Broker上的时候,他接收到了一条消息,接着他会对这个消息做什么事情?首先第一步,他会把这个消息直接写入磁盘上的一个日志文件,叫做CommitLog,直接顺序写入这个文件
  • 这个CommitLog是很多磁盘文件,每个文件限定最多1GB,Broker收到消息之后就直接追加写入这个文件的末尾,就跟上面的图里一样。如果一个CommitLog写满了1GB,就会创建一个新的CommitLog文件。

RocketMq是如何写入数据的

设定一个topic -> 根据设定的MessageQueue个数 -> 分不在不同的master Broker里边 -> 每个MessageQueue是由多个 CommitLog组成 -> Commit是采用顺序读写。加上OS PageCache来保证写入性能 -> 首先。OS PageCache是基于内存的缓冲池。采用异步刷盘或者同步刷盘顺序写入磁盘 (异步刷盘宕机是会有可能导致数据丢失的

  • DLedger 相当于替换了 CommitLog
  • DLedger CommitLog 来构建出机器上的MessageQueue
  • Broker机器刚刚启动的时候,就是靠这个DLedger基于Raft协议实现的leader选举机制,互相投票选举出来一个Leader,其他人就是Follower,然后只有Leader可以接收数据写入,Follower只能接收Leader同步过来的数据
  • DLedger收到一条数据之后,会标记为uncommitted状态,然后他会通过自己的DLedgerServer组件把这个uncommitted数据发送给Follower Broker的DLedgerServer。
  • 接着Follower Broker的DLedgerServer收到uncommitted消息之后,必须返回一个ack给Leader Broker的DLedgerServer,然后如果Leader Broker收到超过半数的Follower Broker返回ack之后,就会将消息标记为committed状态。
  • 也就是说。当leaderBroker收到消息之后会同步给 FollowerBroker 节点。当节点响应ack之后主节点才会返回给生产者ack

源码索引

  • 消息发送
  • producer.send(msg);
  • -> defaultMQProducerImpl.sendDefaultImpl
    • -> this.tryToFindTopicPublishInfo 从 NameService 获取 Topic路由信息(本地有缓存就从缓存中获取)
    • -> this.selectOneMessageQueue 选择一个消息队列 queue
    • -> this.sendKernelImpl 调用发送核心方法
    • -> mQClientFactory.getMQClientAPIImpl().sendMessage 进行发送
  • -> MQClientAPIImpl#sendMessageSync
  • -> remotingClient.invokeSync 调用netty方法发送 RequestCode.SEND_MESSAGE 消息
  • broker接受到消息的处理
  • -> NettyServerHandler#channelRead0
  • -> NettyRemotingAbstract#processMessageReceived
  • -> NettyRemotingAbstract#processRequestCommand 处理客户端的请求消息
  • -> processor.asyncProcessRequest 客户端发送的是异步消息,不需要同步返回成功
  • -> SendMessageProcessor#asyncProcessRequest 进入消息处理
  • -> AbstractSendMessageProcessor#parseRequestHeader 解析请求
  • -> SendMessageProcessor#asyncSendMessage 异步保存发送的消息
  • -> this.brokerController.getMessageStore().asyncPutMessage(msgInner) MessageStore存储消息
  • -> MessageStore#asyncPutMessage 异步保存发送的消息
  • -> MessageStore#putMessage 保存发送的消息
  • -> DefaultMessageStore#asyncPutMessages DefaultMessageStore保存消息默认实现
  • -> this.commitLog.asyncPutMessages(messageExtBatch) 保存发送的消息
  • -> CommitLog#asyncPutMessages 保存发送的消息
  • -> mappedFile.appendMessages(messageExtBatch, this.appendMessageCallback, putMessageContext) mappedFile
  • -> CommitLog#submitFlushRequest 提交刷盘 (异步 / 同步)
  • -> CommitLog#submitReplicaRequest 将消息同步到从节点。可配置备份多少个
  • -> 消息保存完毕

rocketMq 同步消息的原理

  • netty调用方会触发的动作
  • RemotingClient#invokeSync
  • RemotingClient#invokeSyncImpl 发送同步方法的实现
  • this.responseTable.put(opaque, responseFuture) 这个很关键,将opaque 存到响应的 responseTable里边
  • 然后下方 responseFuture.waitResponse(timeoutMillis) 会阻塞当前请求
  • netty被调用方会触发的动作
  • NettyRemotingAbstract 然后我们看此处的方法。
  • RemotingResponseCallback callback = new RemotingResponseCallback() 构建了一个远程
  • 如果请求是同步请求的话,一定会触发 callback.callback(response);
代码语言:javascript
复制
final RemotingResponseCallback callback = new RemotingResponseCallback() {
    @Override
    public void callback(RemotingCommand response) {
        doAfterRpcHooks(remoteAddr, cmd, response);
        if (!cmd.isOnewayRPC()) {
            if (response != null) {
                response.setOpaque(opaque);
                response.markResponseType();
                try {
                    ctx.writeAndFlush(response);
                } catch (Throwable e) {
                    log.error("process request over, but response failed", e);
                    log.error(cmd.toString());
                    log.error(response.toString());
                }
            } else {
            }
        }
    }
};
  • 请观察一下这个的实现。 response.setOpaque(opaque); 想当于将请求的 opaque 塞入到了response里边。
  • 然后将 ctx.writeAndFlush(response); 到调用方
  • 然后回到调用方
  • NettyRemotingAbstract#processMessageReceived 检查到是 RESPONSE_COMMAND 响应的请求
  • responseFuture.putResponse 会设置 responseCommand 并且 countDownLatch.countDown 释放之前阻塞的请求
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-11-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 创建Topic的时候为何要指定MessageQueue数量?
  • 生产者发送消息的时候写入哪个MessageQueue?
  • 如果某个Broker出现故障该怎么办?
  • RocketMQ 是如何持久化消息的
    • 1、为什么Broker数据存储是最重要的一个环节
      • 2、CommitLog消息顺序写入机制
      • RocketMq是如何写入数据的
      • 源码索引
        • rocketMq 同步消息的原理
        相关产品与服务
        对象存储
        对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档