【万字长文】Doris FE启动流程源码详细解析

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
全局流量管理 GTM,标准版 1个月
简介: 【万字长文】Doris FE启动流程源码详细解析


一、简介

Apache Doris是一个现代化的MPP分析型数据库产品。仅需亚秒级响应时间即可获得查询结果,有效地支持实时数据分析。Apache Doris的分布式架构非常简洁,易于运维,并且可以支持10PB以上的超大数据集。

Apache Doris可以满足多种数据分析需求,例如固定历史报表,实时数据分析,交互式数据分析和探索式数据分析等。令您的数据分析工作更加简单高效!

二、名词解释

  • FE:Frontend,即 Doris 的前端节点。主要负责接收和返回客户端请求、元数据以及集群管理、查询计划生成等工作。
  • BE:Backend,即 Doris 的后端节点。主要负责数据存储与管理、查询计划执行等工作。
  • bdbje:Oracle Berkeley DB Java Edition (opens new window)。在 Doris 中,我们使用 bdbje 完成元数据操作日志的持久化、FE 高可用等功能。

三、流程图


640.jpg


四、源码分析

下载Doris源码详细步骤:https://doris.apache.org/zh-CN/developer-guide/fe-idea-dev.html#_1-%E7%8E%AF%E5%A2%83%E5%87%86%E5%A4%87

Doris FE启动步骤

我们先看看 FE启动类代码:

if (Strings.isNullOrEmpty(dorisHomeDir)) {
          System.err.println("env DORIS_HOME is not set.");
          return;
      }
      if (Strings.isNullOrEmpty(pidDir)) {
          System.err.println("env PID_DIR is not set.");
          return;
      }
      CommandLineOptions cmdLineOpts = parseArgs(args);
      try {
          // 创建 pid 文件
          if (!createAndLockPidFile(pidDir + "/fe.pid")) {
              throw new IOException("pid file is already locked.");
          }
          // 初始化 config文件
          Config config = new Config();
          config.init(dorisHomeDir + "/conf/fe.conf");
          // Must init custom config after init config, separately.
          // Because the path of custom config file is defined in fe.conf
          config.initCustom(Config.custom_config_dir + "/fe_custom.conf");
          LdapConfig ldapConfig = new LdapConfig();
          if (new File(dorisHomeDir + "/conf/ldap.conf").exists()) {
              ldapConfig.init(dorisHomeDir + "/conf/ldap.conf");
          }
          // check it after Config is initialized, otherwise the config 'check_java_version' won't work.
          if (!JdkUtils.checkJavaVersion()) {
              throw new IllegalArgumentException("Java version doesn't match");
          }
          Log4jConfig.initLogging(dorisHomeDir + "/conf/");
          // set dns cache ttl
          java.security.Security.setProperty("networkaddress.cache.ttl", "60");
          // check command line options
          checkCommandLineOptions(cmdLineOpts);
          LOG.info("Palo FE starting...");
          //FE Address 初始化
          FrontendOptions.init();
          // 检查端口是否正常
          checkAllPorts();
          if (Config.enable_bdbje_debug_mode) {
              // Start in BDB Debug mode
              BDBDebugger.get().startDebugMode(dorisHomeDir);
              return;
          }
          // 初始化 Catelog 并且等待加载完成
          Catalog.getCurrentCatalog().initialize(args);
          Catalog.getCurrentCatalog().waitForReady();
          // 第一步 启动 HttpServer 类
          // 第二步 启动 FeServer 类
          // 第三步 启动 QeService
          QeService qeService = new QeService(Config.query_port, Config.mysql_service_nio_enabled, ExecuteEnv.getInstance().getScheduler());
          FeServer feServer = new FeServer(Config.rpc_port);
          feServer.start();
          HttpServer httpServer = new HttpServer();
          httpServer.setPort(Config.http_port);
          httpServer.setMaxHttpPostSize(Config.jetty_server_max_http_post_size);
          httpServer.setAcceptors(Config.jetty_server_acceptors);
          httpServer.setSelectors(Config.jetty_server_selectors);
          httpServer.setWorkers(Config.jetty_server_workers);
          httpServer.setMaxThreads(Config.jetty_threadPool_maxThreads);
          httpServer.setMaxThreads(Config.jetty_threadPool_minThreads);
          httpServer.start();
          qeService.start();
          ThreadPoolManager.registerAllThreadPoolMetric();
          while (true) {
              Thread.sleep(2000);
          }
      } catch (Throwable e) {
          e.printStackTrace();
      }

通过上面代码,我们可以清楚了解FE启动时主要执行以下过程:

  • 初始化 Catalog ,并且等待Catelog加载完成
  • 创建 QeServer ,负责与mysql client 通信
  • 创建 FeServer ,由Thrift Server组成,负责 FE 和 BE 通信
  • 创建 HttpServer ,负责提供Rest API以及Doris FE前端页面接口

CataLog 源码解析

CataLog 主要职责是维护FE 元数据,接下来我们看看FE启动时,CataLog初始化时,做什么处理:

// 获取本地节点和helper节点信息
     getSelfHostPort();
     getHelperNodes(args);
     // 检查meta文件目录是否创建
     File meta = new File(metaDir);
     if (!meta.exists()) {
         LOG.warn("Doris' meta dir {} does not exist. You need to create it before starting FE", meta.getAbsolutePath());
         throw new Exception(meta.getAbsolutePath() + " does not exist, will exit");
     }
     //检查 BDB和Image目录是否创建
     if (Config.edit_log_type.equalsIgnoreCase("bdb")) {
         File bdbDir = new File(this.bdbDir);
         if (!bdbDir.exists()) {
             bdbDir.mkdirs();
         }
         File imageDir = new File(this.imageDir);
         if (!imageDir.exists()) {
             imageDir.mkdirs();
         }
     } else {
         throw new Exception("Invalid edit log type: " + Config.edit_log_type);
     }
     // 初始化插件管理
     pluginMgr.init();
     auditEventProcessor.start();
     // 2.获取集群ID和角色(Observer or Follower)
     getClusterIdAndRole();
     // 3. 首次加载image文件和回放Elog日志
     this.editLog = new EditLog(nodeName);
     loadImage(this.imageDir); // 加载image文件
     editLog.open(); // 夹杂bdb环境配置
     this.globalTransactionMgr.setEditLog(editLog);
     this.idGenerator.setEditLog(editLog);
     // 4. 创建加载和导出作业标签清理Daemon线程
     createLabelCleaner();
     // 5. 创建事务清理Daemon线程
     createTxnCleaner();
     // 6. 开始监听线程状态(MASTER/FOLLOWER/OBSERVER状态转换,以及leader选举工作和元数据同步工作)
     createStateListener();
     listener.start();

通过上面源码,我们可以发现,CateLog初始化时,执行以下操作:

  • 首先对Image镜像文件读取数据,和对Elog进行回放操作。
  • 创建加载和导出作业标签清理Daemon线程
  • 创建事务清理Daemon线程
  • 开始监听线程状态(MASTER/FOLLOWER/OBSERVER状态转换,以及leader选举工作和元数据同步工作)「(后续更新一篇文章,专门说元数据同步和Leader选举流程源码解析)」

QeServer 源码解析

QeServer职责是与Mysql Client进行通讯,支持Socket和Nio连接,具体源码:

try {
         HelpModule.getInstance().setUpModule();
     } catch (Exception e) {
         LOG.error("Help module failed, because:", e);
     }
     this.port = port;
     if (nioEnabled) {
         mysqlServer = new NMysqlServer(port, scheduler);
     } else {
         mysqlServer = new MysqlServer(port, scheduler);
     }

当nioEnabled(可配置) 为true时,使用Nio进行通讯,采用这种方式通信的好处是:

  • 同步非阻塞IO
  • IO是面向流的,NIO是面向缓冲区的
  • NIO引入了选择器的概念,选择器用于监听多个通道的事件


FeServer 源码解析

FeServer职责是负责FE和BE之间通信。

try {
            switch (type) {
                case SIMPLE:
                    createSimpleServer();
                    break;
                case THREADED_SELECTOR:
                    createThreadedServer();
                    break;
                default:
                    createThreadPoolServer();
            }
        } catch (TTransportException ex) {
            LOG.warn("create thrift server failed.", ex);
            throw new IOException("create thrift server failed.", ex);
        }
        ThriftServerEventProcessor eventProcessor = new ThriftServerEventProcessor(this);
        server.setServerEventHandler(eventProcessor);
        serverThread = new Thread(new Runnable() {
            @Override
            public void run() {
                server.serve();
            }
        });
        serverThread.setDaemon(true);
        serverThread.start();

FE的Thrift使用的服务模型分为三种:

  • SIMPLE:一般不适用于生产环境,仅限于测试使用。
  • THREADED_SELECTOR:非阻塞式I/O模型,即主从 Reactor 模型,该模型能及时响应大量的并发连接请求,在多数场景下有较好的表现。
  • THREAD_POOL:阻塞式I/O模型,使用线程池处理用户连接,并发连接数受限于线程池的数量,如果能提前预估并发请求的数量,并且能容忍足够多的线程资源开销,该模型会有较好的性能表现,默认使用该服务模型

HttpServer 源码解析

HttpServer职责主要是为Rest API和doris Web页面提供接口服务,源码如下:

Map<String, Object> properties = new HashMap<>();
     properties.put("server.port", port);
     properties.put("server.servlet.context-path", "/");
     properties.put("spring.resources.static-locations", "classpath:/static");
     properties.put("spring.http.encoding.charset", "UTF-8");
     properties.put("spring.http.encoding.enabled", true);
     properties.put("spring.http.encoding.force", true);
     //enable jetty config
     properties.put("server.jetty.acceptors", this.acceptors);
     properties.put("server.jetty.max-http-post-size", this.maxHttpPostSize);
     properties.put("server.jetty.selectors", this.selectors);
     //Worker thread pool is not set by default, set according to your needs
     if(this.workers > 0) {
         properties.put("server.jetty.workers", this.workers);
     }
     // This is to disable the spring-boot-devtools restart feature.
     // To avoid some unexpected behavior.
     System.setProperty("spring.devtools.restart.enabled", "false");
     // Value of `DORIS_HOME_DIR` is null in unit test.
     if (PaloFe.DORIS_HOME_DIR != null) {
         System.setProperty("spring.http.multipart.location", PaloFe.DORIS_HOME_DIR);
     }
     System.setProperty("spring.banner.image.location", "doris-logo.png");
     if (FeConstants.runningUnitTest) {
         // this is currently only used for unit test
         properties.put("logging.config", getClass().getClassLoader().getResource("log4j2.xml").getPath());
     } else {
         properties.put("logging.config", Config.custom_config_dir + "/" + SpringLog4j2Config.SPRING_LOG_XML_FILE);
     }
     new SpringApplicationBuilder()
             .sources(HttpServer.class)
             .properties(properties)
             .run(new String[]{});

HttpServer继承了SpringBootServletInitializer,同时使用了SpringApplicationBuilder类,那么我们就可以很清楚知道,使用Springboot框架提供Rest Api服务。

五、问题思考

  1. CataLog 如何对Elog进行回放?
  2. ELog 日志如何实现数据同步?
  3. BDB 如何存储元数据的?
  4. 说说Doris FE Leader选举流程
  5. Doris FE Leader节点如何判断non-Leader节点是否在线?leader和non-leader长时间失联会导致non-leader提供过期元数据,此问题如何解决?
相关文章
|
29天前
|
监控 安全 开发工具
鸿蒙HarmonyOS应用开发 | HarmonyOS Next-从应用开发到上架全流程解析
HarmonyOS Next是华为推出的最新版本鸿蒙操作系统,强调多设备协同和分布式技术,提供丰富的开发工具和API接口。本文详细解析了从应用开发到上架的全流程,包括环境搭建、应用设计与开发、多设备适配、测试调试、应用上架及推广等环节,并介绍了鸿蒙原生应用开发者激励计划,帮助开发者更好地融入鸿蒙生态。通过DevEco Studio集成开发环境和华为提供的多种支持工具,开发者可以轻松创建并发布高质量的鸿蒙应用,享受技术和市场推广的双重支持。
313 11
|
26天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
26天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
26天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
2天前
|
自然语言处理 数据处理 索引
mindspeed-llm源码解析(一)preprocess_data
mindspeed-llm是昇腾模型套件代码仓,原来叫"modelLink"。这篇文章带大家阅读一下数据处理脚本preprocess_data.py(基于1.0.0分支),数据处理是模型训练的第一步,经常会用到。
9 0
|
1月前
|
域名解析 弹性计算 安全
阿里云服务器租用、注册域名、备案及域名解析完整流程参考(图文教程)
对于很多初次建站的用户来说,选购云服务器和注册应及备案和域名解析步骤必须了解的,目前轻量云服务器2核2G68元一年,2核4G4M服务器298元一年,域名注册方面,阿里云推出域名1元购买活动,新用户注册com和cn域名2年首年仅需0元,xyz和top等域名首年仅需1元。对于建站的用户来说,购买完云服务器并注册好域名之后,下一步还需要操作备案和域名绑定。本文为大家展示阿里云服务器的购买流程,域名注册、绑定以及备案的完整流程,全文以图文教程形式为大家展示具体细节及注意事项,以供新手用户参考。
|
1月前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
27天前
|
安全 搜索推荐 数据挖掘
陪玩系统源码开发流程解析,成品陪玩系统源码的优点
我们自主开发的多客陪玩系统源码,整合了市面上主流陪玩APP功能,支持二次开发。该系统适用于线上游戏陪玩、语音视频聊天、心理咨询等场景,提供用户注册管理、陪玩者资料库、预约匹配、实时通讯、支付结算、安全隐私保护、客户服务及数据分析等功能,打造综合性社交平台。随着互联网技术发展,陪玩系统正成为游戏爱好者的新宠,改变游戏体验并带来新的商业模式。
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
108 2
|
3月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
93 0

热门文章

最新文章

推荐镜像

更多