Kitex原始碼閱讀——腳手架程式碼是如何通過命令列生成的(二)

白澤來了發表於2022-05-28

前言

Kitex是位元組跳動內部的Golang微服務RPC框架,先已開源。

Kitex文件:https://www.cloudwego.io/zh/docs/kitex/getting-started/

Kitex體驗:https://juejin.cn/post/7098966260502921230

Kitex原始碼閱讀—腳手架程式碼的生成(一):https://juejin.cn/post/7100867939829563422

在Kitex體驗的文章中,我們使用Kitex從零構建了自己的服務,只要定義好IDL(介面描述語言),按照Kitex提供的命令列規則,就可以生成支援ThriftProtobuf的客戶端和服務端相關的腳手架程式碼,使得我們可以直接著手編寫服務端的響應實現和客戶端的請求發起邏輯。

這篇文章我們繼續探究Kitex究竟是怎麼生成腳手架程式碼的,通過上篇文章的分析,已經明白main.go檔案中,init()函式的作用是註冊命令解析引數flag,提供給後續使用Go原生命令列解析庫flag做解析。接下來我們繼續分析main.go檔案中剩餘的部分,探究如何在解析得到命令列輸入引數之後,根據引數內容完成腳手架程式碼的自動生成。

提醒:Kitex原始碼閱讀系列的文章連貫性很大,建議按順序閱讀。並且隨著原始碼的閱讀,我會從零開始,不斷擴充套件我們自己基於Kitex編寫的kitexx框架的功能(目前kitexx已經擁有了解析命令列引數的功能)

原始碼分析

分析main.go的main()函式

通過觀察註釋,main函式分為兩個部分,下面分析。

以外掛模式執行

image-20220526144428523

這一部分是一個switch結構,獲取命令列的第一個引數值,如果滿足則case條件則會呼叫給定的Run()方法,完成初始化,並且執行完成後退出程式。雖然我們還沒繼續深入,但是可以猜測,這裡kitex整合了protocthriftgo建立客戶端服務端腳手架的功能,這裡根據命令列需求去呼叫對應程式碼生成邏輯。

只有在不滿足這兩個case的情況下,main函式繼續執行,執行kitex自己的腳手架程式碼生成邏輯。這裡我們先著重於分析kitex自己邏輯,越過這個部分

以kitex模式執行

image-20220526150849105

分析args.parseArgs()函式

  • 用於解析命令列引數,並且對屬於的命令列引數進行有效性檢查,可以說整個args.parseArgs()都在完成各種檢查。

image-20220526155539546

關於a.buildFlags()方法這裡再說明一下,所有能在命令列輸入的flag引數都是事先註冊在FlagSet中的,並且賦予預設值,隨之使用flag庫解析命令列輸入的flag和緊跟著的value之後,將會用輸入value替換註冊的flag預設值,完成解析後,這些flagvalue已經儲存了你需要建立的服務的各種資訊,只要提供給腳手架生成部分的程式碼使用即可。

  • 這裡著重分析一下args.parseArgs()內最後路徑檢查方法a.checkPath的原始碼,因為其包含的內容較多。

carbon (1)

通過分析checkPath()函式,可以找到Kitex文件中對應的下方-module引數需要擇情況新增的原因,針對兩種模式管理的go專案(go path / go mod)kitex工具採用不同的路徑管理邏輯(因為最終要確定腳手架程式碼生成的位置)。

image-20220526172249512

分析buildCmd()函式

image-20220527160800831

cmd := buildCmd(&args, out)是main函式體完成引數初始化檢查後的核心部分,下面將具體分析:

image-20220527165720125

由於buildCmd()函式中lookupTool()函式比較重要,這裡先深入分析:

image-20220527180839087

再來看buildCmd()函式:

carbon

關於thriftgo:因為位元組內部使用RPC的IDL為thrift格式,因此用Go語言實現了自己的thrift編譯器thriftgo,它有著與apache/thrift編譯工具相似的命令列介面,並且通過外掛機制對其進行了增強。

image-20220527184426370

thriftgo地址:https://github.com/cloudwego/thriftgo

protoc則是對應於protobuf格式IDL的編譯器,這樣是使用Google原生的沒有對其增強。

最後再呼叫cmd.Run()方法,則將執行這個定義好的外部命令。(要確保thriftgo編譯工具已經安裝)

image-20220527184045191

小結

這裡整體梳理一下通過命令列生成IDL定製的腳手架的過程:

  • FlagSet註冊會有哪些命令列引數會被輸入(flag鍵值對)
  • 解析輸入的flag鍵值對並且儲存,並且對其進行語法檢查
  • FlagSet中獲取輸入的引數,封裝成外部命令Cmd,用於呼叫thriftgo或者protoc的命令
  • cmd.Run()在指定的檔案路徑中生成客戶端和服務端腳手架程式碼kitex_gen

當然在我們分析main函式原始碼的時候,沒有分析kitex命令列工具作為外掛的工作流程,這裡希望你觸類旁通,嘗試自己去分析一下。

image-20220527190303831

為kitexx增加腳手架程式碼生成功能

編寫kitexx框架

首先要確保你已經安裝了thriftgo編譯工具,然後編寫kitexx工具的主函式,它的作用就是先通過kitexx命令列獲取到g引數,指定thriftgo將要編譯IDL的生成語言為go,然後就是將這些引數構建一個外部命令去呼叫thriftgo編譯工具,在指定的資料夾構建腳手架程式碼。

image-20220527193708804

這裡為什麼我知道驅動thriftgo編譯工具最少只需要-g引數呢?因為thriftgo的程式碼倉庫給出了最簡單的使用方式:

image-20220527194453689

編寫IDL

然後使用go build -o ~/go/bin/kitexx命令將其編譯成一個可執行檔案到$GOPATH/bin目錄下,接著編寫IDL檔案echo.thrift,這裡我使用thrift格式

image-20220527194002662

測試kitexx功能

然後在命令列輸入命令:kitexx -g go,就會在控制檯顯示:

image-20220527194716462

並且在當前目錄下生成了gen-go檔案,其中包含了生成的腳手架程式碼,當然由於kitexx功能過於簡單,我們輸入的引數也僅僅只有一個-g,難免生成的腳手架十分單薄,但我們的目的已經達到了。

image-20220527194804150

總結

通過第二篇文章的講解,已經很清晰的介紹了kitex工具是如何通過命令列,生成go語言RPC服務的腳手架程式碼的(小結部分我已經有所概括),並且我們也自研了擁有腳手架生成功能的kitexx微框架,通過實踐印證了我們對原始碼的理解。

\

相關文章