新手阅读 Nebula Graph 源码的姿势

摘要:在本文中,我们将通过数据流快速学习 Nebula Graph,以用户在客户端输入一条 nGQL 语句 SHOW SPACES 为例,使用 GDB 追踪语句输入时 Nebula Graph 是怎么调用和运行的。

首发于 Nebula Graph 博客:https://nebula-graph.com.cn/posts/how-to-read-nebula-graph-source-code/

阅读源码

导读

对于一些刚开始接触 Nebula Graph 开源库的小伙伴来说,刚开始可能和我一样,想要提高自己,看看大神们的代码然后试着能够做点什么,或许能够修复一个看起来并不是那么困难的 Bug。但是面对如此多的代码,我裂开了,不知道如何下手。最后硬着头皮,再看了一遍又一遍代码,跑了一个又一个用例之后终于有点眉目了。

下面就分享下个人学习 Nebula Graph 开源代码的过程,也希望刚接触 Nebula Graph 的小伙伴能够少走弯路,快速入门。另外 Nebula Graph 本身也用到了一些开源库,详情可以见附录。

在本文中,我们将通过数据流快速学习 Nebula Graph,以用户在客户端输入一条 nGQL 语句 SHOW SPACES 为例,使用 GDB 追踪语句输入时 Nebula Graph 是怎么调用和运行的。

整体架构

整体架构

一个完整的 Nebula Graph 包含三个服务,即 Query Service,Storage Service 和 Meta Service。每个服务都有其各自的可执行二进制文件。

Query Service 主要负责

  • 客户端连接的管理
  • 解析来自客户端的 nGQL 语句为抽象语法树 AST,并将抽象树 AST 解析成一系列执行动作。
  • 对执行动作进行优化
  • 执行优化后的执行计划

Storage Service 主要负责

  • 数据的分布式存储

Meta Service 主要负责

  • 图 schema 的增删查改
  • 集群的管理
  • 用户鉴权

这次,我们主要对 Query Service 进行分析

目录结构

刚开始,可以拿到一个 source 包,解压,可以先看看代码的层级关系,不同的包主要功能是干什么的 下面只列出 src 目录:

|--src
    |--client // 客户端代码
    |--common // 提供一些常用的基础组件
    |--console
    |--daemons
    |--dataman
    |--graph // 包含了Query Service的大部分代码                         
    |--interface // 主要是一些 meta、storage 和 graph 的通讯接口定义     
    |--jni
    |--kvstore
    |--meta // 元数据管理相关 
    |--parser // 主要负责词法和语法分析       
    |--storage // 存储层相关
    |--tools
    |--webservice

代码跟踪

通过 scripts 目录下的脚本启动 metad 和 storaged 这两个服务:

阅读源码

启动后通过 nebula.service status all 查看当前的服务状态

阅读源码

然后 gdb 运行 bin 目录下的 nebula-graphd 二进制程序

gdb> set args --flagfile  /home/mingquan.ji/1.0/nebula-install/etc/nebula-graphd.conf   //设置函数入参
gdb> set follow-fork-mode child   // 由于是守护进程,所以在 fork 子进程后 gdb 继续跟踪子进程
gdb> b main         // 在 mian 入口打断点

在 gdb 中输入 run 开始运行 nebula-graphd 程序,然后通过 next 可以一步一步运行,直到遇到 gServer->serve(); // Blocking wait until shut down via gServer->stop(),此时 nebula-graphd 的所有线程阻塞,等待客户端连接,这时需要找到客户端发起请求后由哪个函数处理。

由于 Nebula Graph 使用 FBThrift 来定义生成不同服务的通讯代码,在 src/interface/graph.thrift 文件中可以看到 GraphService 接口的定义如下:

service GraphService {
    AuthResponse authenticate(1: string username, 2: string password)
    oneway void signout(1: i64 sessionId)
    ExecutionResponse execute(1: i64 sessionId, 2: string stmt)
}

gServer->serve() 之前有

auto interface = std::make_shared<GraphService>();
status = interface->init(ioThreadPool);
gServer->setInterface(std::move(interface));
gServer->setAddress(localIP, FLAGS_port);

可以知道是由 GraphService 对象来处理客户端的连接和请求,因此可以在 GraphService.cpp:``future_execute 处打断点,以便跟踪后续处理流程。

此时重新打开一个终端进入 nebula 安装目录,通过 ./nebule -u=root -p=nebula 来连接 nebula 服务,再在客户端输入 SHOW SPACES ,此时客户端没有反应,是因为服务端还在阻塞调试中,回到服务端输入 continue,如下所示:

阅读源码

经过 session 验证后,进入 executionEngine->execute() 中,step 进入函数内部

auto plan = new ExecutionPlan(std::move(ectx));
plan->execute();

继续 step 进入ExecutionPlanexecute 函数内部,然后执行到

auto result = GQLParser().parse(rctx->query());

parse 这块主要使用 flex & bison,用于词法分析和语法解析构造对象到抽象语法树,其词法文件是 src/parser/scanner.lex,语法文件是 src/parser/parser.yy,其词法分析类似于正则表达式,语法分析举例如下:

go_sentence
    : KW_GO step_clause from_clause over_clause where_clause yield_clause {
        auto go = new GoSentence();
        go->setStepClause($2);
        go->setFromClause($3);
        go->setOverClause($4);
        go->setWhereClause($5);
        if ($6 == nullptr) {
            auto *cols = new YieldColumns();
            for (auto e : $4->edges()) {
                if (e->isOverAll()) {
                    continue;
                }
                auto *edge  = new std::string(*e->edge());
                auto *expr  = new EdgeDstIdExpression(edge);
                auto *col   = new YieldColumn(expr);
                cols->addColumn(col);
            }
            $6 = new YieldClause(cols);
        }
        go->setYieldClause($6);
        $$ = go;
    }

其在匹配到对应到 go 语句时,就构造对应的节点,然后由 bison 处理,最后生成一个抽象的语法树。

词法语法分析后开始执行模块,继续 gdb,进入 excute 函数,一直 step 直到进入ShowExecutor::execute 函数。

阅读源码

继续 next 直到 showSpaces()step 进入此函数

auto future = ectx()->getMetaClient()->listSpaces();
auto *runner = ectx()->rctx()->runner();
'''
'''
std::move(future).via(runner).thenValue(cb).thenError(error);

此时 Query Service 通过 metaClient 和 Meta Service 通信拿到 spaces 数据,之后通过回调函数 cb 回传拿到的数据,至此 nGQL 语句 SHOW SPACES; 已经执行完毕,而其他复杂的语句也可以以此类推。

  • 如果是正在运行的服务,可以先查出该服务的进程 ID,然后通过 gdb attach PID 来调试该进程;
  • 如果不想启动服务端和客户端进行调试,在 src 目录下的每个文件夹下都有一个 test 目录,里面都是对对应模块或者功能进行的单元测试,可以直接编译对应的单元模块,然后跟踪运行。方法如下:
    1. 通过对应目录下的 CMakeLists.txt 文件找到对应的模块名
    2. 在 build 目录下 make 模块名,在 build/bin/test 目录下生成对应的二进制程序
    3. gdb 跟踪调试该程序

附录

阅读 Nebula Graph 源码需要了解的一些库:

  1. flex & bison:词法分析和语法分析工具,将客户端输入的 nGQL 语句解析为抽象语法树
  2. FBThrift:Facebook 开源的 RPC 框架,定义并生成了 Meta 层、Storage 层和 Graph 层的通讯过程代码
  3. folly:Facebook 开源的 C++14 组件库,提供了类似 Boost 和 std 库的功能,在性能上更加优化
  4. Gtest:Google 开源的 C++ 单元测试框架

其中数据库资料可以参考:

  1. 数据库基本介绍
  2. SQL调优
  3. Nebula 架构剖析系列(零)图数据库的整体架构设计

喜欢这篇文章?来来来,给我们的 GitHub 点个 star 表鼓励啦~~ 🙇‍♂️🙇‍♀️ [手动跪谢]

交流图数据库技术?交个朋友,Nebula Graph 官方小助手微信:NebulaGraphbot 拉你进交流群~~

作者有话说:Hi,我是明泉,是图数据 Nebula Graph 研发工程师,主要工作和数据库查询引擎相关,希望本次的经验分享能给大家带来帮助,如有不当之处也希望能帮忙纠正,谢谢~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260