新手閱讀 Nebula Graph 原始碼的姿勢
阿新 • • 發佈:2020-08-05
> 摘要:在本文中,我們將通過資料流快速學習 Nebula Graph,以使用者在客戶端輸入一條 nGQL 語句 `SHOW SPACES` 為例,使用 GDB 追蹤語句輸入時 Nebula Graph 是怎麼呼叫和執行的。
> 首發於 Nebula Graph 部落格:https://nebula-graph.com.cn/posts/how-to-read-nebula-graph-source-code/
![閱讀原始碼](https://www-cdn.nebula-graph.com.cn/nebula-blog/source-code.png)
## 導讀
對於一些剛開始接觸 [Nebula Graph](https://0x7.me/sourcecode2github) 開源庫的小夥伴來說,剛開始可能和我一樣,想要提高自己,看看大神們的程式碼然後試著能夠做點什麼,或許能夠修復一個看起來並不是那麼困難的 Bug。但是面對如此多的程式碼,我裂開了,不知道如何下手。最後硬著頭皮,再看了一遍又一遍程式碼,跑了一個又一個用例之後終於有點眉目了。
下面就分享下個人學習 Nebula Graph 開原始碼的過程,也希望剛接觸 Nebula Graph 的小夥伴能夠少走彎路,快速入門。另外 Nebula Graph 本身也用到了一些開源庫,詳情可以見附錄。
在本文中,我們將通過資料流快速學習 Nebula Graph,以使用者在客戶端輸入一條 nGQL 語句 `SHOW SPACES` 為例,使用 GDB 追蹤語句輸入時 Nebula Graph 是怎麼呼叫和執行的。
## 整體架構
![整體架構](https://www-cdn.nebula-graph.com.cn/nebula-blog/architecture.png)
一個完整的 Nebula Graph 包含三個服務,即 Query Service,Storage Service 和 Meta Service。每個服務都有其各自的可執行二進位制檔案。
Query Service 主要負責
- 客戶端連線的管理
- 解析來自客戶端的 nGQL 語句為抽象語法樹 AST,並將抽象樹 AST 解析成一系列執行動作。
- 對執行動作進行優化
- 執行優化後的執行計劃
Storage Service 主要負責
- 資料的分散式儲存
Meta Service 主要負責
- 圖 schema 的增刪查改
- 叢集的管理
- 使用者鑑權
這次,我們主要對 Query Service 進行分析
## 目錄結構
剛開始,可以拿到一個 source 包,解壓,可以先看看程式碼的層級關係,不同的包主要功能是幹什麼的 下面只列出 src 目錄:
```markdown
|--src
|--client // 客戶端程式碼
|--common // 提供一些常用的基礎元件
|--console
|--daemons
|--dataman
|--graph // 包含了Query Service的大部分程式碼
|--interface // 主要是一些 meta、storage 和 graph 的通訊介面定義
|--jni
|--kvstore
|--meta // 元資料管理相關
|--parser // 主要負責詞法和語法分析
|--storage // 儲存層相關
|--tools
|--webservice
```
## 程式碼跟蹤
通過 scripts 目錄下的指令碼啟動 metad 和 storaged 這兩個服務:
![閱讀原始碼](https://www-cdn.nebula-graph.com.cn/nebula-blog/console.png)
啟動後通過 `nebula.service status all` 檢視當前的服務狀態
![閱讀原始碼](https://www-cdn.nebula-graph.com.cn/nebula-blog/service-status.png)
然後 gdb 執行 bin 目錄下的 `nebula-graphd` 二進位制程式
```cpp
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 介面的定義如下:
```cpp
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()` 之前有
```cpp
auto interface = std::mak