广告
返回顶部
首页 > 资讯 > 后端开发 > Python >RocketMQ设计之同步刷盘
  • 587
分享到

RocketMQ设计之同步刷盘

2024-04-02 19:04:59 587人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

同步刷盘方式:在返回写成功状态时,消息已经被写入磁盘。具体流程是,消息写入内存的PAGECACHE后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,返回消

同步刷盘方式:在返回写成功状态时,消息已经被写入磁盘。具体流程是,消息写入内存的PAGECACHE后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,返回消息写成功的状态。

在同步刷盘模式下,当消息写到内存后,会等待数据写到磁盘的CommitLog文件。

CommitLog的handleDiskFlush方法:

public void handleDiskFlush(AppendMessageResult result, PutMessageResult putMessageResult, 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());
            service.putRequest(request);
            boolean flushOK = request.waitForFlush(this.defaultMessageStore.getMessageStoreConfig().getSyncFlushTimeout());
            if (!flushOK) {
                log.error("do groupcommit, wait for flush failed, topic: " + messageExt.getTopic() + " tags: " + messageExt.getTags()
                    + " client address: " + messageExt.getBornHostString());
                putMessageResult.setPutMessageStatus(PutMessageStatus.FLUSH_DISK_TIMEOUT);
            }
        } else {
            service.wakeup();
        }
    }
    // Asynchronous flush
    else {
        if (!this.defaultMessageStore.getMessageStoreConfig().isTransientStorePoolEnable()) {
            flushCommitLogService.wakeup();
        } else {
            commitLogService.wakeup();
        }
    }
}


class GroupCommitService extends FlushCommitLogService {
        private volatile List<GroupCommitRequest> requestsWrite = new ArrayList<GroupCommitRequest>();
        private volatile List<GroupCommitRequest> requestsRead = new ArrayList<GroupCommitRequest>();

        //提交刷盘任务到任务列表
        public synchronized void putRequest(final GroupCommitRequest request) {
            synchronized (this.requestsWrite) {
                this.requestsWrite.add(request);
            }
            if (hasNotified.compareAndSet(false, true)) {
                waitPoint.countDown(); // notify
            }
        }

        private void swapRequests() {
            List<GroupCommitRequest> tmp = this.requestsWrite;
            this.requestsWrite = this.requestsRead;
            this.requestsRead = tmp;
        }

        private void doCommit() {
            synchronized (this.requestsRead) {
                if (!this.requestsRead.isEmpty()) {
                    for (GroupCommitRequest req : this.requestsRead) {
                        // There may be a message in the next file, so a maximum of
                        // two times the flush
                        boolean flushOK = false;
                        for (int i = 0; i < 2 && !flushOK; i++) {
                            flushOK = CommitLog.this.mappedFileQueue.getFlushedWhere() >= req.getNextOffset();

                            if (!flushOK) {
                                CommitLog.this.mappedFileQueue.flush(0);
                            }
                        }

                        req.wakeupCustomer(flushOK);
                    }

                    long storeTimestamp = CommitLog.this.mappedFileQueue.getStoreTimestamp();
                    if (storeTimestamp > 0) {
                        CommitLog.this.defaultMessageStore.getStoreCheckpoint().setPhysicMsgTimestamp(storeTimestamp);
                    }

                    this.requestsRead.clear();
                } else {
                    // Because of individual messages is set to not sync flush, it
                    // will come to this process
                    CommitLog.this.mappedFileQueue.flush(0);
                }
            }
        }

        public void run() {
            CommitLog.log.info(this.getServiceName() + " service started");

            while (!this.isStopped()) {
                try {
                    this.waitForRunning(10);
                    this.doCommit();
                } catch (Exception e) {
                    CommitLog.log.warn(this.getServiceName() + " service has exception. ", e);
                }
            }

            // Under nORMal circumstances shutdown, wait for the arrival of the
            // request, and then flush
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                CommitLog.log.warn("GroupCommitService Exception, ", e);
            }

            synchronized (this) {
                this.swapRequests();
            }

            this.doCommit();

            CommitLog.log.info(this.getServiceName() + " service end");
        }

        @Override
        protected void onWaitEnd() {
            this.swapRequests();
        }

        @Override
        public String getServiceName() {
            return GroupCommitService.class.getSimpleName();
        }

        @Override
        public long getJointime() {
            return 1000 * 60 * 5;
        }
    }

GroupCommitRequest是刷盘任务,提交刷盘任务后,会在刷盘队列中等待刷盘,而刷盘线程

GroupCommitService每隔10毫秒写一批数据到磁盘。之所以不直接写是磁盘io压力大,写入性能低,每隔10毫秒写一次可以提升磁盘io效率和写入性能。

  • putRequest(request) 提交刷盘任务到任务列表
  • request.waitForFlush同步等待GroupCommitService将任务列表中的任务刷盘完成。

两个队列读写分离,requestsWrite是写队列,用户保存添加进来的刷盘任务,requestsRead是读队列,在刷盘之前会把写队列的数据放入读队列。

CommitLog的doCommit方法:

private void doCommit() {
            synchronized (this.requestsRead) {
                if (!this.requestsRead.isEmpty()) {
                    for (GroupCommitRequest req : this.requestsRead) {
                        // There may be a message in the next file, so a maximum of
                        // two times the flush
                        boolean flushOK = false;
                        for (int i = 0; i < 2 && !flushOK; i++) {
                            //根据offset确定是否已经刷盘
                            flushOK = CommitLog.this.mappedFileQueue.getFlushedWhere() >= req.getNextOffset();

                            if (!flushOK) {
                                CommitLog.this.mappedFileQueue.flush(0);
                            }
                        }

                        req.wakeupCustomer(flushOK);
                    }

                    long storeTimestamp = CommitLog.this.mappedFileQueue.getStoreTimestamp();
                    if (storeTimestamp > 0) {
                        CommitLog.this.defaultMessageStore.getStoreCheckpoint().setPhysicMsgTimestamp(storeTimestamp);
                    }
                    //清空已刷盘的列表
                    this.requestsRead.clear();
                } else {
                    // Because of individual messages is set to not sync flush, it
                    // will come to this process
                    CommitLog.this.mappedFileQueue.flush(0);
                }
            }
        }
  • 刷盘的时候依次读取requestsRead中的数据写入磁盘,
  • 写入完成后清空requestsRead

读写分离设计的目的是在刷盘时不影响任务提交到列表。

CommitLog.this.mappedFileQueue.flush(0);是刷盘操作:

public boolean flush(final int flushLeastPages) {
    boolean result = true;
    MappedFile mappedFile = this.findMappedFileByOffset(this.flushedWhere, this.flushedWhere == 0);
    if (mappedFile != null) {
        long tmpTimeStamp = mappedFile.getStoreTimestamp();
        int offset = mappedFile.flush(flushLeastPages);
        long where = mappedFile.getFileFromOffset() + offset;
        result = where == this.flushedWhere;
        this.flushedWhere = where;
        if (0 == flushLeastPages) {
            this.storeTimestamp = tmpTimeStamp;
        }
    }

    return result;
}

通过MappedFile映射的CommitLog文件写入磁盘

这就是RocketMQ高可用设计之同步刷盘的基本情况了,大体思路就是一个读写分离的队列来刷盘,同步刷盘任务提交后会在刷盘队列中等待刷盘完成后再返回,而GroupCommitService每隔10毫秒写一批数据到磁盘。

到此这篇关于RocketMQ设计之同步刷盘的文章就介绍到这了,更多相关RocketMQ同步刷盘内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: RocketMQ设计之同步刷盘

本文链接: https://www.lsjlt.com/news/143219.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • RocketMQ设计之同步刷盘
    同步刷盘方式:在返回写成功状态时,消息已经被写入磁盘。具体流程是,消息写入内存的PAGECACHE后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,返回消...
    99+
    2022-11-13
  • RocketMQ设计之异步刷盘
    上一篇RocketMQ设计之同步刷盘 异步刷盘方式:在返回写成功状态时,消息可能只是被写入了内存的PAGECACHE,写操作的返回快,吞吐量大;当内存里的消息量积累到一定程度时,统一...
    99+
    2022-11-13
  • RocketMQ设计之同步刷盘的示例分析
    这篇文章主要介绍RocketMQ设计之同步刷盘的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!在同步刷盘模式下,当消息写到内存后,会等待数据写到磁盘的CommitLog文件。CommitLog的handleD...
    99+
    2023-06-29
  • RocketMQ设计之故障规避机制
    NameServer为了简化和客户端通信,发现Broker故障时并不会立即通知客户端。故障规避机制就是用来解决当Broker出现故障,Producer不能及时感知而导致消息发送失败的...
    99+
    2022-11-13
  • 浅谈Java并发之同步器设计
    前言: 在 Java并发内存模型详情了解到多进程(线程)读取共享资源的时候存在竞争条件。 计算机中通过设计同步器来协调进程(线程)之间执行顺序。同步器作用就像登机安检人员一样可以协...
    99+
    2022-11-12
  • RocketMQ设计之主从复制和读写分离
    目录一、主从复制二、读写分离一、主从复制 RocketMQ为了提高消费的高可用性,避免Broker发生单点故障引起Broker上的消息无法及时消费,同时避免单个机器上硬盘坏损出现消费...
    99+
    2022-11-13
  • RocketMQ设计之故障规避机制的示例分析
    这篇文章给大家分享的是有关RocketMQ设计之故障规避机制的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。NameServer为了简化和客户端通信,发现Broker故障时并不会立即通知客户端。故障规避机...
    99+
    2023-06-29
  • Java并发之同步器设计的方法是什么
    本篇内容介绍了“Java并发之同步器设计的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在 Java并发之内存模型了解到多进程(线...
    99+
    2023-06-16
  • RocketMQ之NameServer架构设计及启动关闭流程源码分析
    目录NameServer1.架构设计2.核心类与配置NamesrvControllerNamesrvConfigNettyServerConfigRouteInfoManager3....
    99+
    2022-11-12
  • Co-PLAN-协同计划盘口架设平台
    Co-PLAN(协同计划平台)以先进的项目管理和目标绩效管理思想为指导,面向制造业企业,将企业的项目管理、月度目标管理和绩效考核管理紧密融合,规范项目管理流程,协助实现从项目立项、计划、执行、监控和结项的...
    99+
    2022-10-18
  • 如何从RocketMQ消息持久化设计看磁盘性能瓶颈的突破
    今天就跟大家聊聊有关如何从RocketMQ消息持久化设计看磁盘性能瓶颈的突破,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。从RocketMQ消息持久化设计看磁盘性能瓶颈的突破分布式消...
    99+
    2023-06-19
  • [平台建设] 日志数据同步数仓设计
    本文主要针对日志数据接入数据仓库场景进行设计, 同时介绍了下在设计接入时的一些细节,针对可能出现的问题进行必要的处理. 背景 主要针对用户流量数据、风控数据、人物画像等数据进行同步至数仓, 制...
    99+
    2019-01-19
    [平台建设] 日志数据同步数仓设计
  • C++算法设计之马踏棋盘的实现
    本文实例为大家分享了C++算法设计之马踏棋盘的具体代码,供大家参考,具体内容如下 (一)马踏棋盘经典算法描述:   (1)马踏棋盘是经典的程序设计问题之一,主要的解决方案有...
    99+
    2022-11-13
  • Java架构设计之六步拆解DDD
    目录引言项目需求信息DDD落地实践战略设计1、业务分析(1)事前准备(2)邀请参会的人(3)业务讨论2、领域建模(1)领域对象分析(2)构建业务聚合3、划分边界上下文战术设计1、微服...
    99+
    2022-11-13
  • 设计 | ClickHouse 分布式表实现数据同步
    作者:吴帆 青云数据库团队成员 主要负责维护 MySQL 及 ClickHouse 产品开发,擅长故障分析,性能优化。 在多副本分布式 ClickHouse 集群中,通常需要使用 Distributed 表写入或读取数据,Distr...
    99+
    2021-07-11
    设计 | ClickHouse 分布式表实现数据同步
  • 设计自动监控从MySQL同步的脚本方法
    下面讲讲关于设计自动监控从MySQL同步的脚本方法,文字的奥妙在于贴近主题相关。所以,闲话就不谈了,我们直接看下文吧,相信看完设计自动监控从MySQL同步的脚本方法这篇文章你一定会有所受益。  &...
    99+
    2022-10-18
  • [离线计算-Spark|Hive] 数据近实时同步数仓方案设计
    本文主要针对hudi进行调研, 设计MySQL CDC 近实时同步至数仓中方案, 写入主要利用hudi的upsert以及delete能力. 针对hudi 表的查询,引入kyuubi 框架,除 了增强平台 ...
    99+
    2015-04-26
    [离线计算-Spark|Hive] 数据近实时同步数仓方案设计
  • Java毕业设计实战之在线网盘系统的实现
    一、项目简述 功能:用户的邮箱注册、验证码验证以及用户登录。 不需要注册账号,也可以上传满足条件的临时文件,但是只4小时内有效。 文件的管理,上传、下载、重命名、删除、查看统计数据、...
    99+
    2022-11-13
  • Java 设计模式之责任链模式及异步责任链详解
    目录一、定义二、普通责任链模式三、异步责任链模式一、定义 责任链模式(Chain of Responsibility Pattern):避免将一个请求的发送者与接受者耦合在一起,让多...
    99+
    2022-11-12
  • 如何设计一个优化的MySQL表结构来实现数据同步功能?
    如何设计一个优化的MySQL表结构来实现数据同步功能?数据同步是在分布式系统中非常常见的需求,它可以确保多个节点之间的数据一致性。在MySQL中,我们可以通过合理设计表结构来实现数据同步功能。本文将介绍如何设计一个优化的MySQL表结构,并...
    99+
    2023-10-31
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作