做者:amchtml
引言:上一篇文章介紹瞭如何建立安裝 TarsGo,同時也闡述瞭如何開始一個 TarsGo HTTP 服務。本文就要開始 TarsGo 的主力業務了:基於 TARS 自帶的 RPC 協議,設計 TarsGo 服務。
本文的內容大體思路與官方 Quick Start 相同,但例子會有所不一樣,同時對於一些坑也會解釋得詳細點。本文的代碼能夠在個人 GitHub repo 中找到。前端
上一篇文章中,個人 HTTP 服務器向前端返回一串 Json 字符串,其中包含了服務器時間。這一次,我設計一個服務(命名爲amc.GoTarsServer.GoTarsObj
)來提供服務器時間。HTTP 服務則向這個新服務請求時間以後再返回給用戶。前文提到 HTTP 服務的實例名稱相對不過重要,可是供內部 RPC 調用的服務,其名稱就很重要了,它是其餘服務進行尋址的重要依據。c++
TARS 框架的原生 RPC 調用是使用專門設計的 tars 協議
(文件後綴名 .tars
)進行通訊的。這個協議其實也不神祕,讀者能夠自行嘗試一下、多看一些示例,很快就能夠了解了。這裏我按照我本身寫的協議文件來講明吧:git
// filename: DateTime.tars module amc { struct GetTimeReq { 0 optional string timeFmt; }; struct GetTimeRsp { 0 require long utcTimestamp; // UTC UNIX timestamp 1 require long localTimestamp; 2 require string localTimeStr; }; interface DateTime { int GetTime(GetTimeReq req, out GetTimeRsp rsp); }; };
上面的協議中,其實包含了幾個部分:github
struct
定義複合數據類型因此,解讀上面的協議,以下:web
DateTime
下,定義了一個方法:GetTime
timeFmt
,表示以什麼樣的格式返回時間信息UTC
時間戳、本地時間戳和時間字符串int MethodName(MethodReq req, out MethodRsq rsp)
的模式來命名,不管是否有入參和出參,方法中的 req
和 rsp
都會存在。這種設計方式比較適合將來的擴展,若是須要添加參數或返回信息,只須要在兩個 struct
中添加便可。require
屬性,表示該參數是必須的;可是在之後擴展協議時,新增參數應設置爲 optional
屬性,保證還未升級到新版本協議的 clients 仍能正常調用。首先,咱們能夠用 TarsGo 自帶的工具首先生成工程模版:json
cd $GOPATH/src/github.com/TarsCloud/TarsGo/tars/tools chmod +x create_tars_server.sh ./create_tars_server.sh amc GoTarsServer GoTars
執行腳本後,在相應目錄下會生成必要的源文件:segmentfault
$ cd ~/go/src/amc/GoTarsServer $ ls -l total 36 -rw-rw-r-- 1 centos centos 159 Jan 7 00:00 GoTars.tars -rw-rw-r-- 1 centos centos 303 Jan 7 00:00 gotars_imp.go -rw-rw-r-- 1 centos centos 964 Jan 7 00:00 config.conf -rw-rw-r-- 1 centos centos 422 Jan 7 00:00 main.go drwxrwxr-x 2 centos centos 4096 Jan 7 00:00 client drwxrwxr-x 2 centos centos 4096 Jan 7 00:00 debugtool -rw-rw-r-- 1 centos centos 252 Jan 7 00:00 makefile -rw-rw-r-- 1 centos centos 59 Jan 7 00:00 start.sh drwxrwxr-x 2 centos centos 4096 Jan 7 00:00 vendor
其中 GoTars.tars
文件,咱們就不須要了,用上面的 DateTime.tars
文件替換之。接着,咱們使用 TarsGo 的工具,將協議文件轉換爲源文件:centos
cd ~/go/src/amc/GoTarsServer tars2go DateTime.tars
執行後,tars2go
會在當前目錄下,根據 .tars
文件中指定的 module
字段,生成一個新的目錄。好比上面的協議文件,module
是 amc
,那麼 tars2go
就生成 amc
目錄。讀者能夠自行查看目錄下的文件,若是 .tars
文件更新的話,須要再次執行 tars2go
命令刷新相應的文件——固然,我以爲徹底能夠調整 makefile
的邏輯來自動實現這一點。服務器
協議的實現,在 gotars_imp.go
文件中實現。下面我只列出該文件中實現的主要部分:
package main import ( "fmt" "time" "context" "github.com/TarsCloud/TarsGo/tars" amc "amc/GoTarsServer/amc" ) // GoTarsImp servant implementation type GoTarsImp struct {} // 獲取log對象 var log = tars.GetLogger("logic") func (imp *GoTarsImp) GetTime(ctx context.Context, req *amc.GetTimeReq, rsp *amc.GetTimeRsp) (int32, error) { // Note 4 // get timestamp utc_time := time.Now() local_time := utc_time.Local() // convert time string var time_str string if "" == (*req).TimeFmt { log.Debug("Use default time format") time_str = local_time.Format("01/02 15:04:05 2006") } else { log.Debug(fmt.Sprintf("Got format string: %s", (*req).TimeFmt)) // ...... // ...... time_str = local_time.Format(time_str) } // construct response (*rsp).UtcTimestamp = utc_time.Unix() (*rsp).LocalTimestamp = local_time.Unix() (*rsp).LocalTimeStr = time_str return 0, nil };
針對代碼裏的幾個註釋說明以下:
tars2go
所生成的 amc
目錄下的 go
文件。經過導入該包,咱們就能夠 access 到咱們在前面的 .tars
文件中所定義的結構體和方法。這裏實際上是寫了一個基於 $GOPATH
的絕對路徑來存取該包。/usr/local/app/tars/app_log/amc/GoTarsServer/
目錄下生成日誌文件。好比我用的 log 文件名就是:amc.GoTarsServer_logic.log
。.tars
文件中 GetTime
的實現,它做爲 GoTarsImp
對象的一個方法來實現。從返回值的角度,TarsGo RPC 方法的返回值除了協議中定義的(本例中是 int
,對應於 Go 的 int32
)以外,還有一個 error
,若是須要的話,讀者能夠利用。細心的讀者可能會發現,在上面的實現中,數據變量名和協議中定義的並不相同。是的,這就是剛轉 Go 的開發者很容易遇到的坑之一:Go 語言是使用變量 / 方法 / 常量的命名方式來決定其可見性的,只有在首字母爲大寫的時候,該元素才能供外部訪問。
筆者特地在 .tars
文件中,變量名採用了首字母小寫的駝峯式命名法。讀者能夠看到,tars2go
會自動將變量名和方法名的首字母改成大寫,以保證其可見性。請開發者注意,不然會在編譯時遇到未定義錯誤。
如今讓咱們回到 main.go
文件。其實工具自動生成的代碼就差很少了,須要修改的是包導入的部分和新建app
對象語句,改成以下所示:
import ( "fmt" "os" "github.com/TarsCloud/TarsGo/tars" amc "amc/GoTarsServer/amc" ) func main() { // Get server config cfg := tars.GetServerConfig() imp := new(GoTarsImp) err := imp.Init() if err != nil { fmt.Printf("GoTarsImp init fail, err:(%s)\n", err) os.Exit(-1) } // 默認爲amc.GoTars,因爲使用了DateTime.tars,須要修改成amc.DateTime app := new(amc.DateTime) app.AddServantWithContext(imp, cfg.App+"."+cfg.Server+".GoTarsObj") tars.Run() }
其餘不變。
這裏我選擇了上一篇文章中提到的 GoWebServer
來調用這個 TARS 服務。這裏咱們就須要將已有的代碼進行改造了。須要改造的代碼是 goweb_imp.go
文件 :
package main import ( "fmt" "net/http" "github.com/TarsCloud/TarsGo/tars" amc "amc/GoTarsServer/amc" ) func HttpRootHandler(w http.ResponseWriter, r *http.Request) { comm := tars.NewCommunicator() app := new(amc.DateTime) obj := "amc.GoTarsServer.GoTarsObj" comm.SetProperty("locator", "tars.tarsregistry.QueryObj@tcp -h 192.168.211.128 -p 17890") req := amc.GetTimeReq{} rsp := amc.GetTimeRsp{} req.TimeFmt = "" comm.StringToProxy(obj, app) _, err := app.GetTime(&req, &rsp) if err != nil { // ...... 系統錯誤處理 } else { // ...... 從 rsp 中取出問題 } ret_str := fmt.Sprintf("{\"msg\":\"Hello, Tars-Go!\", \"time\":\"%s\"}", rsp.LocalTimeStr) w.Header().Set("Content-Type", "application/json;charset=utf-8") w.Write([]byte(ret_str)) return }
主要邏輯的說明以下:
192.168.211.128
和 17890
是 TARS 主控 tarsregistry
的地址,能夠經過TarsWeb界面查看服務發佈的方法在前一篇文章已經說明了。GoWebServer
只須要在原有基礎上作更新操做便可。本文的 GoTarsServer
也同理。不一樣的是在 協議
選項,應該選擇 TARS
。
服務發佈、一切正常後,參照上一篇文章,再次訪問 HTTP 服務,而後咱們再查看 GoTarsServer
的 log,咱們就能夠看到二者已經成功地聯繫起來啦。
TARS能夠在考慮到易用性和高性能的同時快速構建系統並自動生成代碼,幫助開發人員和企業以微服務的方式快速構建本身穩定可靠的分佈式應用,從而令開發人員只關注業務邏輯,提升運營效率。多語言、敏捷研發、高可用和高效運營的特性使 TARS 成爲企業級產品。
TARS微服務助您數字化轉型,歡迎訪問:
TARS官網:https://TarsCloud.org
TARS源碼:https://github.com/TarsCloud
獲取《TARS官方培訓電子書》:https://wj.qq.com/s2/6570357/...
或掃碼獲取: