1. 程式人生 > >Kite: 一個分散式微服務框架(翻譯)

Kite: 一個分散式微服務框架(翻譯)

> 原文連結:https://blog.gopheracademy.com/birthday-bash-2014/kite-microservice-library/ 此為中文翻譯 用GO語言來編寫web服務是一件很輕鬆的事。簡單而又強大的`net/http`包允許你以一種快速的方式編寫高效能的web服務。然而,有時候你僅僅想要編寫一個RPC後端應用。本質上,你想有很多獨立執行的應用程式,他們各自負責自己的那塊工作。他們應當接收請求並恰當的回覆。 很顯然,一旦脫離了基本的需求,事情就變得複雜了。在真實場景中,你可能擁有數百個正在執行的web服務,並希望能和他們安全的(並經過身份驗證)通訊交流。為了達成這一目的,首先必須與某一個應用建立連線。除非你只有很少的幾個應用節點,你很難記住某個特定應用的IP地址或hostname(有太多應用)。僅僅把所有host的IP地址持久化儲存也是不夠的,因為host IP可能改變。你需要的是一個能讓你訪問、詢問並取得某應用IP地址的服務,就像DNS伺服器。 所以說搭建一個有許多應用的分散式系統比較難。[Koding](https://www.koding.com/)的[Kite](https://github.com/koding/kite)庫旨在以一種簡單快捷輕便的方式搭建分散式微服務應用。Kite框架本身有很多細節部分,在這篇文章中只會大概闡述Kite能幹什麼。 ### Kite介紹 Kite是一個用GO語言編寫的微服務RPC框架,它使得使用者能編寫清晰易懂的分散式系統。它在便捷使用和效能之間找到了一個平衡。Kite既是一個RPC伺服器又是客戶端。它能與其它的Kite同伴進行雙向通訊。一個Kite節點由以下引數確定(順序很重要): 1. Username: Kite的擁有者,比如 Brian, Fatih, Damian etc.. 2. Environment: 當前環境,比如 “production”, “testing”, “staging”, etc… 3. Name: 標識Kite型別的簡稱,比如 mykite,fs,terminal, etc… 4. Version: 三位數的語義版本(semantic version) 5. Region: 當前地區,比如 “Europe”, “Asia” 或其他地方 6. Hostname: Kite的hostname 7. ID: 唯一ID,用來確定一個Kite。由Kite框架生成,也可以自行更改 這些識別符號很重要因為Kite就是通過他們來讓他人鑑別和搜尋自己。 Kite使用[SockJS](https://github.com/sockjs/sockjs-client)在很多不同傳輸方法(websocket, xhr, etc..)提供WebSocket模擬(emulation ),這意味著你也可以通過瀏覽器來連結Kite(見[Kite.js](https://github.com/koding/kite.js))。Kite使用修改過的[dnode protocal](https://github.com/substack/dnode-protocol)來進行RPC訊息傳遞。Kite協議增加了一個額外的session和authentication層,這樣就能輕鬆地識別Kite。在後臺,它使用JWT進行身份驗證和會話資訊管理。 通過“Kontrol”這個服務發現機制,一個Kite可以發現其他Kites與他們安全地進行身份驗證通訊。Kontrol使用[etcd](https://github.com/coreos/etcd)作為後臺儲存。但是你也用其他的替代(當前支援[PostgreSQL](https://www.postgresql.org/)),只要它實現了 [kontrol.Storage](http://godoc.org/github.com/koding/kite/kontrol#Storage)介面。Kontrol同時也有許多認證使用者的方式。這是可定製的所以人們能用自己方式使用Kontrol。 ### 如何使用Kite 我們現在來學習一下。編寫Kite並讓他們之間通訊很有趣。首先,介紹一個最簡單的形式(原諒我忽略了錯誤處理,你不應該像我這樣:)) ```go package main import "github.com/koding/kite" func main() { k := kite.New("first", "1.0.0") k.Run() } ``` 這裡我們建立了一個kite,它的名字是first,版本是1.0.0。`Run()`方法用來啟動一個阻塞伺服器(就像`http.Serve`)。它現在能夠接受請求。由於沒分配埠號,作業系統會自動為我們分配一個。 現在我們分配一個埠號,這樣我們就能使另一個kite和他連線(否則,你需要從日誌中選擇分配的URL)。為了更改Kite配置,例如埠號,一些屬性(Environemt, Region, etc...),你需要更改`Config`域: ```go package main import "github.com/koding/kite" func main() { k := kite.New("first", "1.0.0") k.Config.Port = 6000 k.Run() } ``` 如有需要,配置值也可以被環境變數覆蓋。 讓我們建立第二個kite來和第一個kite通訊: ```go package main import ( "fmt" "github.com/koding/kite" ) func main() { k := kite.New("second", "1.0.0") client := k.NewClient("http://localhost:6000/kite") client.Dial() response, _ := client.Tell("kite.ping") fmt.Println(response.MustString()) } ``` 這回我們直接連線新的kite,因為我們已經知道了URL。對於一個RPC系統,你得有URL路徑的概念。Kite使用方法名來讓別人呼叫。每個方法對應一個Handle(就像`http.Handler`)。Kite框架有一些預設的方法,其中一個就是`kite.ping`,它返回一個`pong`字串作為響應(他不需要任何身份驗證資訊)。響應可以是任何東西,從能被序列化的GO型別到JSON,這取決於傳送方。Kite也有一些預先定義好的輔助方法來把響應轉換成給定型別。在這個例子裡,second kite 和 first kite 連線並呼叫了first kite 的 `kite.ping`方法。我們沒有傳遞任何引數(下面將解釋),所以如果你執行,可以看到: ```shell $ go run second.go pong ``` ### 為Kite新增方法 現在我們新增第一個自定義方法。這個方法用來接收一個值並返回它的平方值。方法名字是`square`。要將函式分配給方法,只需確保其滿足 `kite.Handler`介面[kite.Handler](https://godoc.org/github.com/koding/kite#Handler) : ```go package main import "github.com/koding/kite" func main() { k := kite.New("first", "1.0.0") k.Config.Port = 6000 k.Config.DisableAuthentication = true k.HandleFunc("square", func(r *kite.Request) (interface{}, error) { a := r.Args.One().MustFloat64() return a * a, nil }) k.Run() } ``` 通過 second kite 呼叫: ```go package main import ( "fmt" "github.com/koding/kite" ) func main() { k := kite.New("second", "1.0.0") client := k.NewClient("http://localhost:6000/kite") client.Dial() response, _ := client.Tell("square", 4) fmt.Println(response.MustFloat64()) } ``` 可以看到,唯一改變的是方法呼叫。呼叫 "square" 方法也傳遞了引數4。執行例子得到: ```go $ go run second.go 16 ``` 就這麼簡單。 ### 服務發現,如何找到對方 服務發現被整合到了Kite框架中。就像前面所說,這是一個非常基本的概念,並且在Kite API也得到了充分體現。這意味著Kite框架強制使用者使用服務發現機制。為了能發現自己,對方要知道你的真實身份。也就是說你需要進行身份驗證。身份驗證可以通過多種方法完成,這取決於Kontrol怎麼執行。它可以被完全禁用,可以詢問使用者密碼(通過kite cli),可以獲取令牌並驗證使用者提供的內容等等。 `kitectl`是一個方便的CLI程式,可用於通過命令列輕鬆管理kites。我們可以用它(通過 `kitectl register` 命令)來向Kontrol認證我們的host,所以I每個執行在我們host的kite例項將預設被驗證。這個命令在home目錄下建立`kite.key`檔案,它由kontrol自己簽名認證。其中內容沒有加密,但是因為已簽名,所以可以用它和Kontrol安全交流。我們的使用者名稱會被儲存到Kontrol中,所以其他人可以信任我們(當然他們得使用同一個Kontrol伺服器)。相信Kontrol意味著可以相信任何人。這很重要因為可能會有其他的Kontrol伺服器,他們也在你的內網中或者是公開的。 我們將使用先前的例子,不過這次會把 first kite 註冊到Kontrol並從 second kite 取得它的IP地址: ```go package main import ( "net/url" "github.com/koding/kite" ) func main() { k := kite.New("first", "1.0.0") k.Config.Port = 6000 k.HandleFunc("square", func(r *kite.Request) (interface{}, error) { a := r.Args.One().MustFloat64() return a * a, nil }) k.Register(&url.URL{Scheme: "http", Host: "localhost:6000/kite"}) k.Run() } ``` 如你所見, 我們用`Register()`方法把自己註冊到Kontrol中。唯一傳遞的引數時我們自己的URL,其他人可以通過它和我們連線。這個值儲存在Kontrol中,其他kite例項可以從那裡獲取到它。`Register()`方法很特殊,如果你斷開連線並重連,它會自動重新註冊。為了保護Kontrol,我們使用了[exponential backoff](http://en.wikipedia.org/wiki/Exponential_backoff)演算法進行重連嘗試。因為Koding在實際生產也大量是用它,所以有許多類似這樣的小細節小改進。另一點是註冊時你不需要傳遞Kontrol的URL,因為你已經通過驗證,Kontrol的URL被存放在`kite.key`中了。你要做的僅僅是呼叫`Register()`方法。 現在我們尋找first kite並呼叫其`square`方法: ```go package main import ( "fmt" "github.com/koding/kite" "github.com/koding/kite/protocol" ) func main() { k := kite.New("second", "1.0.0") // search a kite that has the same username and environment as us, but the // kite name should be "first" kites, _ := k.GetKites(&protocol.KontrolQuery{ Username: k.Config.Username, Environment: k.Config.Environment, Name: "first", }) // there might be several kites that matches our query client := kites[0] client.Dial() response, _ := client.Tell("square", 4) fmt.Println(response.MustFloat64()) } ``` 首先`GetKites()`方法獲取了一個list,其中包含匹配我們查詢的所有kites。`GetKites()`連線到Kontrol並獲取所有URL匹配給定查詢的kites節點。該查詢必須採用樹路徑形式(與etcd中使用的格式相同),所以Username和Environment需要在你搜索first kite之前給定。在這個例子中,我們假定只有一個匹配上了,接著取出它,撥號並呼叫方法,這樣就能得到和之前一樣的結果。 因此,動態註冊和獲取kites是一件大事。你可以設計一個分散式系統,它能容忍你定義的某些條件。一個例子是開啟10個`first` kites,每個都以你的名字命名。如果另一個kite節點從Kontrol中獲取,它會得到一個包含10個kite節點及其URL的list,之後該怎麼做完全取決於這個kite例項。可以隨機挑選一個,也可以輪詢呼叫,抑或是ping所有list裡的kite節點並選取最快的一個等等。 這一切都交給了呼叫方。Kontrol並不知道某個kite例項會有什麼行為,它只知道該節點是否連線(註冊)上了。這樣的簡化讓使用者可以基於該框架構建更復雜的系統。 ### 結論 Kite框架還有許多其它這裡沒涉及的小改進與特性。比如Kite.js可以在瀏覽器上作為客戶端使用。它還包含一個等效node.js的伺服器。它包含開箱即用的通道代理和反向代理,可用於在單個埠/應用後面多路複用kite。Koding正在實際生產中使用它,因此預設情況下它具有許多基於效能的修復和改進。 編寫Kite並使用它是最重要的部分。一旦開始使用它,你就可以感受到API的簡單性。Kite庫易於使用,因為它與Go具有相同的理念。它使用一些用Go編寫的最好的開源專案(例如etcd)。Go使編寫穩定平臺作為Kite庫的基礎變得簡單。由於Go的性質,擴充套件和改進Kite庫也很容易。 希望你對這個框架的想法和意圖及其功能和侷限性有所瞭解。我們正在廣泛使用和維護它。但是,我們也有很多事情想改進(例如提供其他訊息協議和傳輸協議)。盡情fork專案(https://github.com/koding/kite)並隨意使用。歡迎貢獻!讓我知道你的