rocketmq简记

前言

    关于mq的概念、优缺点不再多说,主要谈谈rocketmq的通信和存储,看其源码可知remoting模块就是其通信模块了,也就是netty+protocol,不多说,broker模块就是其服务模块了,负责存储消息响应producer和consumer的请求,了解rocketmq的都知道其主要分3个组件或者说4个组件:Producer、Consumer、Broker、Namesrv,既然说存储,那就谈谈Broker。

码说

    用过rocketmq原生api的都了解其编程模型,大体上就是搞个Producer、设置上namesrvAddr、发送消息就完事了,对于Producer不在这里多说,比较简单,大体就是和namesrv通信选择一个broker,发送消息,也就是相对broker来说就是个客户端(注意对于存储服务来说的),那么broker是如何接收消息、如何存储消息才是我们关心的,因为消息中间件基本功能就是对一方提供写服务,对另一方提供读服务,那只要保证2个条件,基本功能也就具备了,条件就是通信和存储。

那就从通信说起,简单看看BrokerStartup就知道rocketmq是由netty来进行底层通信的,那就先看看Producer发送过来的消息Broker是如何接收处理的,先看看整体流程。

    可以看到netty接收到请求后进行解码,将消息封装成任务交给线程池异步执行,实际由NettyRequestProcessor去处理请求,接下来就是由MessageStore去存储,整体大致就是如此。当然这看起来太粗糙了,通信通信模型还没有说协议,那就看看RemotingCommand,这个对象就是协议的定义了;那么如何存储呢,这个问题还得再细致说说,这里只谈到了由MessageStore去存储,还没谈谈它如何来存储的。

public CompletableFuture<PutMessageResult> asyncPutMessage(MessageExtBrokerInner msg) {
        //省略
        CompletableFuture<PutMessageResult> putResultFuture = this.commitLog.asyncPutMessage(msg);
        //省略
        return putResultFuture;
    }

省略部分代码后就是其存储逻辑了,就是让CommitLog去存储,接下来看看CommitLog如何存储。

public CompletableFuture<PutMessageResult> asyncPutMessage(final MessageExtBrokerInner msg) {
  MappedFile mappedFile = this.mappedFileQueue.getLastMappedFile();
  try {
      if (null == mappedFile || mappedFile.isFull()) {
          mappedFile = this.mappedFileQueue.getLastMappedFile(0);
      }
      result = mappedFile.appendMessage(msg, this.appendMessageCallback);
  }
  CompletableFuture<PutMessageStatus> flushResultFuture = submitFlushRequest(result, msg);
  CompletableFuture<PutMessageStatus> replicaResultFuture = submitReplicaRequest(result, msg);
        
}

可见CommitLog就是从其队列中取尾部的MappedFile,把消息追加到MappedFile,然后进行刷盘,继续看看如何刷盘。这个刷盘说起来话可就长了,还是长话短说吧。

public CompletableFuture<PutMessageStatus> submitFlushRequest(AppendMessageResult result, MessageExt messageExt) {
        // Synchronization flush
        if (FlushDiskType.SYNC_FLUSH == this.defaultMessageStore.getMessageStoreConfig().getFlushDiskType()) {
            final GroupCommitService service = (GroupCommitService) this.flushCommitLogService;
            if (messageExt.isWaitStoreMsgOK()) {
                GroupCommitRequest request = new GroupCommitRequest(result.getWroteOffset() + result.getWroteBytes(),
                        this.defaultMessageStore.getMessageStoreConfig().getSyncFlushTimeout());
                service.putRequest(request);
                return request.future();
            } else {
                service.wakeup();
                return CompletableFuture.completedFuture(PutMessageStatus.PUT_OK);
            }
        }
        // Asynchronous flush
        else {
            if (!this.defaultMessageStore.getMessageStoreConfig().isTransientStorePoolEnable()) {
                flushCommitLogService.wakeup();
            } else  {
                commitLogService.wakeup();
            }
            return CompletableFuture.completedFuture(PutMessageStatus.PUT_OK);
        }
    }

这么一看就是同步异步刷新的问题,咱就先说先异步也就是commitLogService.wakeUp,说到这得看看这个组件了FlushCommitLogService。

 

这一看明白了,就是搞个线程去跑任务呗,那看看FlushRealTimeService咋跑的。

public void run() {
            while (!this.isStopped()) {
                try {
                    CommitLog.this.mappedFileQueue.flush(flushPhysicQueueLeastPages);
                } catch (Throwable e) {
                }
            }
        }

就是循环刷盘呗,当然省略了一部分代码,刷盘是有条件的,这些不是主要的,主要的是mappedFileQueue.flush。

public boolean flush(final int flushLeastPages) {
        boolean result = true;
        MappedFile mappedFile = this.findMappedFileByOffset(this.flushedWhere, this.flushedWhere == 0);
        if (mappedFile != null) {
            int offset = mappedFile.flush(flushLeastPages);
        }
        return result;
    }

就是让MappedFile去刷盘。

public int flush(final int flushLeastPages) {
        if (this.isAbleToFlush(flushLeastPages)) {
            if (this.hold()) {
                int value = getReadPosition();
                try {
                    //We only append data to fileChannel or mappedByteBuffer, never both.
                    if (writeBuffer != null || this.fileChannel.position() != 0) {
                        this.fileChannel.force(false);
                    } else {
                        this.mappedByteBuffer.force();
                    }
                } catch (Throwable e) {
                }
            } else {
            }
        }
        return this.getFlushedPosition();
    }

到这差不多了,就是FileChannel或者MappedByteBuffer去刷盘了。

后语

    回顾下我们都干了些什么,首先想看看Producer发过来的消息Broker是如何处理的(通信、存储),为了搞清楚通信,我们发现了NettyServerHandler这个组件,它拿着消息解码成RemotingCommand并且最终封装成RequestTast提交给BrokerFixedThreadPoolExecutor去执行,接下来看了是如何存储的,发现是MessageStore负责存储,要存储的东西有点多,CommitLog、ConsumeQueue、IndexFile,搞了单独线程去处理刷盘,最终通过FileChannel和MappedByteBuffer去进行刷盘。最后看下broker,主要就是netty通信,然后nio顺序存储。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

&一步

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值