1 | Apache Doris 源码分析1 - 从进程启动到接收处理SQL |
doris 分为 fe 和 be, 其中 fe 主要负责用户交互的部分, 它的启动的入口是 start_fe.sh 脚本, 这个脚本会初始化环境变量, 配置 java home, log dir, 保存 pid 等一系列的操作, 咱们直接看这个脚本文件最后的几行:
1 | //位置: bin/start_fe.sh |
上面这条判断条件会根据是否要以 daemon 方式启动来选择加或不加 nohup, 重点是这个主类:org.apache.doris.PaloFe, 它是 fe 进程的开始, 下面咱们进入这个主类的 main 函数():
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/PaloFe.java |
main() 函数很简单, 把自己的 home-dir 和 pid-dir 以及其它参数传递调用了自己的 start() 函数, 这个 start() 函数会做一系列的初始化工作包括:
- 刚刚传递的参数是否合法
- 尝试锁住自己的 pid 文件
- 从 home-dir/conf/fe.conf 初始化配置
- 从 home-dir/conf/ldap.conf 初始化ldap配置
- 检查 java 版本是否兼容
- 初始化 log4j 配置
- 检查所有端口是否已经被占用
之后, 会进入4项重要的工作:
- 初始化并启动 Catalog 服务
- 初始化并启动 rpc 服务 feServer
- 初始化并启动 http 服务 httpServer2
- 初始化并启动 MySQL Server 服务 qeService
今天分析主线是如何接受到第一条 SQL 语句, 所以重点放在 qeService 上面, 它的启动代码逻辑如下:
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/PaloFe.java |
下面先分析一下 QeService() 的构造函数:
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/qe/QeService.java |
上面的代码片段中, 首先是初始化了帮助模块, 然后判断是否是异步nio模式(默认是)来启动不同的 mysqlServer 实例, 这里继续沿着 nio 的路径往下走, 看一下 NMysqlServer() 的构造函数:
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/mysql/nio/NMysqlServer.java |
上面的代码中又添加了网络连接回调函数acceptListener, 它的构造函数比较简单就不分析了, 关于它的回调handleEvent() 一会儿再分析. 到这里就完成了QeService() 的构造函数逻辑.
下面开始分析 qeService.start() 的逻辑.
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/qe/QeService.java |
下面再看一下 mysqlServer.start() 的逻辑:
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/mysql/nio/NMysqlServer.java |
这里启动了一个异步网络连接服务, 监听指定的端口, 并设置了回调 acceptListener, 然后 server.resumeAccepts()来开始接受网络请求.
以上就是启动时的所有同步逻辑了, 通过这些步骤启动了一个监控 MySQL 端口的 Service. 接下来, 就是用户通过 MySQL Client 来连接这个 Service 时的表现了.
假设有个用户连接到这个 Service 上面了, 在接收到来自用户的网络请求时, 回调函数 acceptListener.handleEvent() 会被调用, 来看一下它的处理逻辑:
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/mysql/nio/AcceptListener.java |
参考上面标记出来的重点, 接下来分析一下 context.startAcceptQuery(processor);的处理逻辑.
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/mysql/nio/NConnectContext.java |
下面继续跟进 mysqlChannel.startAcceptQuery() 内部逻辑.
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/mysql/nio/NMysqlChannel.java |
上面代码中有个很重要的处理逻辑是放了一个 ReadListener 的回调实例, 它会在这个连接接到一个 mysql 命令时被回调, 下面分析一下它被回调的函数 handleEvent().
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/mysql/nio/ReadListener.java |
下面继续跟进上面代码标记出来的重点: connectProcessor.processOnce();
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java |
上面的代码片段, 先会创建一个 MysqlChannel 实例, 然后开始接受数据包, 在接收完成后, 会调用 dispatch() 进行逻辑处理. 下面继续跟进 dispatch().
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java |
在 dispatch() 的处理逻辑, 先是拿一个命令数字, 然后根据它是哪种命令, 进行分别的处理, 命令的类型包含:
- INIT_DB
- QUIT
- QUERY
- FIELD_LIST
- PING
今天要分析的重点是来自用户的第一条查询语句, 所以继续跟进到 QUERY 这个命令的处理逻辑 handleQuery() 里面.
1 | //位置: fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java |
至此, 就完成了 Doris 从启动到接收并处理 SQL 的全流程分析了.
后面的文章, 咱们再深入研究一下词法分析, 语法分析, 语义分析等等一系列的处理步骤的核心源码.