关于nebula:新手阅读-Nebula-Graph-源码的姿势

44次阅读

共计 4013 个字符,预计需要花费 11 分钟才能阅读完成。

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

首发于 Nebula Graph 博客:https://nebula-graph.com.cn/p…

导读

对于一些刚开始接触 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 研发工程师,次要工作和数据库查问引擎相干,心愿本次的教训分享能给大家带来帮忙,如有不当之处也心愿能帮忙纠正,谢谢~

正文完
 0