摘要:在本文中,咱们将经过数据流快速学习 Nebula Graph,以用户在客户端输入一条 nGQL 语句
SHOW SPACES
为例,使用 GDB 追踪语句输入时 Nebula Graph 是怎么调用和运行的。
首发于 Nebula Graph 博客: https://nebula-graph.com.cn/p...
对于一些刚开始接触 Nebula Graph 开源库的小伙伴来讲,刚开始可能和我同样,想要提升本身,看看大神们的代码而后试着可以作点什么,或许可以修复一个看起来并非那么困难的 Bug。可是面对如此多的代码,我裂开了,不知道如何下手。最后硬着头皮,再看了一遍又一遍代码,跑了一个又一个用例以后终于有点眉目了。html
下面就分享下我的学习 Nebula Graph 开源代码的过程,也但愿刚接触 Nebula Graph 的小伙伴可以少走弯路,快速入门。另外 Nebula Graph 自己也用到了一些开源库,详情能够见附录。git
在本文中,咱们将经过数据流快速学习 Nebula Graph,以用户在客户端输入一条 nGQL 语句 SHOW SPACES
为例,使用 GDB 追踪语句输入时 Nebula Graph 是怎么调用和运行的。github
一个完整的 Nebula Graph 包含三个服务,即 Query Service,Storage Service 和 Meta Service。每一个服务都有其各自的可执行二进制文件。web
Query Service 主要负责正则表达式
Storage Service 主要负责数据库
Meta Service 主要负责微信
此次,咱们主要对 Query Service 进行分析markdown
刚开始,能够拿到一个 source 包,解压,能够先看看代码的层级关系,不一样的包主要功能是干什么的 下面只列出 src 目录:session
|--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
进入ExecutionPlan
的 execute
函数内部,而后执行到
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;
已经执行完毕,而其余复杂的语句也能够以此类推。
若是不想启动服务端和客户端进行调试,在 src 目录下的每一个文件夹下都有一个 test 目录,里面都是对对应模块或者功能进行的单元测试,能够直接编译对应的单元模块,而后跟踪运行。方法以下:
阅读 Nebula Graph 源码须要了解的一些库:
其中数据库资料能够参考:
喜欢这篇文章?来来来,给咱们的 GitHub 点个 star 表鼓励啦~~ 🙇♂️🙇♀️ [手动跪谢]
交流图数据库技术?交个朋友,Nebula Graph 官方小助手微信:NebulaGraphbot 拉你进交流群~~
做者有话说:Hi,我是明泉,是图数据 Nebula Graph 研发工程师,主要工做和数据库查询引擎相关,但愿本次的经验分享能给你们带来帮助,若有不当之处也但愿能帮忙纠正,谢谢~