[筆記] 實作分散式計分系統(四) : Build up an interactive CLI with Cobra

R. H.
hobo engineer
Published in
6 min readFeb 9, 2020

Cobra 是 Golang 中相當好用的一套件 ,能幫助我們建立屬於自己的command ,而且非常好上手,以下將直接使用 Cobra 來建立 CLI 去呼叫 上篇 建立的 RPC function。

Step1:

安裝所需套件。

go get -u github.com/spf13/cobra/cobra

Step2:

在 client 資料夾下初始化 cobra 專案 cobra init [pkg-name]
這邊要小小注意的是 pkg-name 僅僅代表該系列的 CLI 名稱,一開始我以為像是常用的 :
git add . git commit -m "xxx" 的 pkg-name 就會是 git ,結果搞錯(冏)
如果想要像 git add . 這樣的效果 ,需要在 rootcmd 下而外綁定 git ,然後再綁定 add 在 git 下這樣的階層式設定。

而這邊一開始先別那樣複雜,先用個 CLI 雛型
new [name] : 新增棒球比賽
list [dbIP] [dbPort] : 獲取比賽列表(可指定 DB Node)
add [id] [team] [round] [add]: 為指定比賽分數新增得分
score [id] [dbIP] [dbPort] : 獲取指定比賽分數(可指定 DB Node)

所以於 client 資料夾下執行

cobra init --pkg-name game
cobra add new
cobra add list
cobra add add
cobra add score

此時資料夾結構

client
│ ├── cmd
│ │ ├── add.go
│ │ ├── list.go
│ │ ├── new.go
│ │ ├── root.go
│ │ └── score.go
│ ├── LICENSE // 自動生成的 沒用到可以刪掉
│ ├── clienNode.go // 先建立一個空的 clientNode.go 待會會用到

Step3:

此時先來看一下程式結構,簡單以 root.go list.go 做說明
(以下省略了部份程式碼,僅以大致架構做說明)

root.go
list.go

這邊不難理解,其實就是以階層式包裝的,首先我們在 root.go 中定義 game command,並於 list.go 中定義 list command,並將其綁訂到 game command 中。
(但我認為在 rootCmd 裡設定 game 並沒有特別用處,因為它代表不是 command 會變成 game list,這時 command 還是 list,如果不了解的看下面 demo 圖會清楚些。)

而基礎的初始化做好後可以先來試用一下, 調用方法也很簡單,僅須呼叫 root.go 下的 Execute() 即可,所以於下 clientNode.go 呼叫 Execute()

clientNode.go — demo

demo1 : 執行 clientNode.go 並帶入參數 --help
會自動秀出相關的操作說明

--help

demo2 : list
會執行 list.go 中對 list 的操作 fmt.Println(“list called”)
但這邊也是我認為 rootCmd 設定並沒有特別用處的原因,因為我們帶入的 command 並不是 game list 而只是 list ,這點我個人認為 Cobra 的設計並不是太直觀

list

Step4:

這一步會跳的比較多一點,但搭配程式碼應該不難理解

  1. 將剛才 client/cmd 下的所有 go file 全部加進 cmd.go 中。
  2. 將 rootCmd 設為空,然後在綁定 gameCmd (這樣才能達到上述
    game list 的效果)。
  3. init()中加入各 command 所需的參數。
  4. scoreCmd listCmd addCmd newGameCmd 實做呼叫 RPC function 的細節。
    new [name] : 新增棒球比賽
    list [dbIP] [dbPort] : 獲取比賽列表(可指定 DB Node)
    add [id] [team] [round] [add]: 為指定比賽分數新增得分
    score [id] [dbIP] [dbPort] : 獲取指定比賽分數(可指定 DB Node)

另外在 demo2 的部份可以看到 Cobra 這樣預設的 CLI 架構基本上還是必須去執行一個 go file 然後帶入 command 參數,像是:
go run clientNode_demo.go list

個人不太喜歡這樣子的作法,因為每操作一次就要去執行一次clientNode.go

這邊希望能以 交談式的循環界面 呈現。而可以發現
go run clientNode_demo.go list 這樣子的呼叫方式是將欲執行的 command 帶入 os.args 中的,所以方法也很簡單在 clientNode.go 的部份改成以循環的方式去讀取 stdin ,並且每次讀取到 stdin 時抽換掉 os.args 即可 ,所以以下最後一步。(當然這作法有點暴力 ,或許有更適合的方式)

5. 更改 cliendNode.go 呼叫 cmd.go - Execute() 的方式,改為循環式讀取 stdin,每讀取一次再去呼叫一次 Execute(),並抽換os.args

6. 最後展示下成果,client 端能以 交談式的循環界面 來去呼叫 RPC function

實做解說的部份在這告個段落,而更多細節處理的部份 (e.g. 包成 container 之類)可參考 Simple_Distributed_System ,完整的專案程式都已經放在那
下方為此部份 code 與資料夾結構 。

├── client
│ ├── clientNode.go
│ └── cmd
│ └── cmd.go
cmd.go
clientNode.go

--

--