rocketmq之源码分析broker之核心存储(二十)

这里根据RocketMQ的前期操作将整理Broker的存储设计及核心层次关系,先上图,根据源码的分析及整理,最终的核心层级关系如下:

1,通信层采用netty来进行网络的接受及处理

2,核心的处理有针对producer的发送服务事件SendMessageProcessor,关于consumer的拉取服务事件PullMessageProcessor

3,无论是哪种基于数据的处理,底层都是基于数据存储的封装对象MessageStore的实现DefaultMessageStore

4,DefaultMessageStore包装了核心的数据结构,罗列如下

    4.1 consumeQueueTable封装了topic对应的queueId及关联的ConsumeQueue,核心是ConsumeQueue的对象,记录着consumer的消费信息

    4.2 CommitLog对象是接受数据的实际操作对象封装,大部分基于数据的操作都是基于该对象

    4.3 IndexService索引服务,特殊功能,如果producer发送时增加properties中增加了keys的操作,会根据设定生成对应的索引,便于基于key的查询

5,分两种情况处理,主要是基于核心业务的操作和基于扩展个性的操作

    5.1 MappedFileQueue对象,内部将所有的存在且可用的文件进行排序保存成有序的队列,便于查找和定位

    5.2 indexFileList本身是集合,核心是IndexFile,集合的意思和上面的队列功能相似,核心实现是IndexFile操作

6,文件映射内存的封装,MappedFile的高性能就体现在这里,提供了顺序写,随机读,内存数据刷盘,清理无关数据,可从该类的属性解读

public class MappedFile extends ReferenceResource {
    public static final int OS_PAGE_SIZE = 1024 * 4;
    protected static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.STORE_LOGGER_NAME);

    private static final AtomicLong TOTAL_MAPPED_VIRTUAL_MEMORY = new AtomicLong(0);

    private static final AtomicInteger TOTAL_MAPPED_FILES = new AtomicInteger(0);
    //当前文件写的位置
    protected final AtomicInteger wrotePosition = new AtomicInteger(0);
    //ADD BY ChenYang
    //当前文件被commit的位置
    protected final AtomicInteger committedPosition = new AtomicInteger(0);
    //即将数据刷到盘的位置
    private final AtomicInteger flushedPosition = new AtomicInteger(0);
    //文件的大小
    protected int fileSize;

    //顶层转换通道
    protected FileChannel fileChannel;
    /**
     * Message will put to here first, and then reput to FileChannel if writeBuffer is not null.
     */
    protected ByteBuffer writeBuffer = null;
    //内存池,根据配置
    protected TransientStorePool transientStorePool = null;
    //文件名
    private String fileName;
    //文件映射的开始位置
    private long fileFromOffset;
    //文件
    private File file;

    //内存映射
    private MappedByteBuffer mappedByteBuffer;
    //存储数据的时间
    private volatile long storeTimestamp = 0;
    //是否是第一次创建
    private boolean firstCreateInQueue = false;

7,根据调用顺序和核心功能解析

    7.1 FileChannel对象,根据jdk中的io转换为nio的channel,便于基于nio的操作,核心转换操作

    7.2 MappedByteBuffer,核心高性能的体现,将整个1G文件的内容映射到当前服务器的虚拟内存中,进行高效io操作

    7.3 ByteBuffer,nio的核心对象,基于缓存的操作,这里说下最大的区别:io是基于单个字节的数据操作,nio是基于数据块的操作

8,这里就是底层服务器的io操作

特别声明:高效的另一个操作就是顺序写和随机读

顺序写:根据源码查看分两种情况,一种是根据用户的请求,内部根据请求计算位置,同时根据内部协议进行序列化后保存数据;第二种是直接提供二进制接口,外部根据协议进行序列化直接调用保存接口

//将消息放置到缓冲区中,接受的回调方法
public AppendMessageResult appendMessagesInner(final MessageExt messageExt, final AppendMessageCallback cb) {
    assert messageExt != null;
    assert cb != null;

    //获取当前的文件写的偏移位置
    int currentPos = this.wrotePosition.get();

    if (currentPos < this.fileSize) {
        ByteBuffer byteBuffer = writeBuffer != null ? writeBuffer.slice() : this.mappedByteBuffer.slice();
        //当前的位置
        byteBuffer.position(currentPos);
        AppendMessageResult result = null;
        //区分单次和批量提交的操作,核心操作在回调接口
        if (messageExt instanceof MessageExtBrokerInner) {
            result = cb.doAppend(this.getFileFromOffset(), byteBuffer, this.fileSize - currentPos, (MessageExtBrokerInner) messageExt);
        } else if (messageExt instanceof MessageExtBatch) {
            result = cb.doAppend(this.getFileFromOffset(), byteBuffer, this.fileSize - currentPos, (MessageExtBatch) messageExt);
        } else {
            return new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR);
        }
        this.wrotePosition.addAndGet(result.getWroteBytes());
        this.storeTimestamp = result.getStoreTimestamp();
        return result;
    }
    log.error("MappedFile.appendMessage return null, wrotePosition: {} fileSize: {}", currentPos, this.fileSize);
    return new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR);
}

public long getFileFromOffset() {
    return this.fileFromOffset;
}

//外部基于保存的序列化传递过来二进制
public boolean appendMessage(final byte[] data) {
    int currentPos = this.wrotePosition.get();

    if ((currentPos + data.length) <= this.fileSize) {
        try {
            this.fileChannel.position(currentPos);
            this.fileChannel.write(ByteBuffer.wrap(data));
        } catch (Throwable e) {
            log.error("Error occurred when append message to mappedFile.", e);
        }
        this.wrotePosition.addAndGet(data.length);
        return true;
    }

    return false;
}

随机读:随机读的操作在该类中同样分两种情况,一种是从指定位置开始读指定大小的数据,第二种是从指定位置开始读取所有数据

public SelectMappedBufferResult selectMappedBuffer(int pos, int size) {
    //获得读取内容的位置
    int readPosition = getReadPosition();
    //读取数据的验证
    if ((pos + size) <= readPosition) {
        //安全的验证
        if (this.hold()) {
            //将原缓冲区中的共享出一个片段,底层数据一致
            ByteBuffer byteBuffer = this.mappedByteBuffer.slice();
            //构造位置
            byteBuffer.position(pos);
            ByteBuffer byteBufferNew = byteBuffer.slice();
            //构造容量
            byteBufferNew.limit(size);
            return new SelectMappedBufferResult(this.fileFromOffset + pos, byteBufferNew, size, this);
        } else {
            log.warn("matched, but hold failed, request pos: " + pos + ", fileFromOffset: "
                + this.fileFromOffset);
        }
    } else {
        log.warn("selectMappedBuffer request pos invalid, request pos: " + pos + ", size: " + size
            + ", fileFromOffset: " + this.fileFromOffset);
    }

    return null;
}

public SelectMappedBufferResult selectMappedBuffer(int pos) {
    //获得读取的位置
    int readPosition = getReadPosition();
    //验证长度
    if (pos < readPosition && pos >= 0) {
        //安全的验证
        if (this.hold()) {
            //将原缓冲区中的共享出一个片段,底层数据一致
            ByteBuffer byteBuffer = this.mappedByteBuffer.slice();
            byteBuffer.position(pos);
            //计算读取数据的大小
            int size = readPosition - pos;
            ByteBuffer byteBufferNew = byteBuffer.slice();
            byteBufferNew.limit(size);
            return new SelectMappedBufferResult(this.fileFromOffset + pos, byteBufferNew, size, this);
        }
    }

    return null;
}

转载于:https://my.oschina.net/wangshuaixin/blog/3061715

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值