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


原文鏈接:https://blog.gopheracademy.com/birthday-bash-2014/kite-microservice-library/ 此為中文翻譯
轉載請注明出處:https://www.cnblogs.com/Ama2ingYJ/p/13307002.html

用GO語言來編寫web服務是一件很輕松的事。簡單而又強大的net/http包允許你以一種快速的方式編寫高性能的web服務。然而,有時候你僅僅想要編寫一個RPC后端應用。本質上,你想有很多獨立運行的應用程序,他們各自負責自己的那塊工作。他們應當接收請求並恰當的回復。

很顯然,一旦脫離了基本的需求,事情就變得復雜了。在真實場景中,你可能擁有數百個正在運行的web服務,並希望能和他們安全的(並經過身份驗證)通信交流。為了達成這一目的,首先必須與某一個應用建立連接。除非你只有很少的幾個應用節點,你很難記住某個特定應用的IP地址或hostname(有太多應用)。僅僅把所有host的IP地址持久化儲存也是不夠的,因為host IP可能改變。你需要的是一個能讓你訪問、詢問並取得某應用IP地址的服務,就像DNS服務器。

所以說搭建一個有許多應用的分布式系統比較難。KodingKite庫旨在以一種簡單快捷輕便的方式搭建分布式微服務應用。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在很多不同傳輸方法(websocket, xhr, etc..)提供WebSocket模擬(emulation ),這意味着你也可以通過瀏覽器來鏈接Kite(見Kite.js)。Kite使用修改過的dnode protocal來進行RPC消息傳遞。Kite協議增加了一個額外的session和authentication層,這樣就能輕松地識別Kite。在后台,它使用JWT進行身份驗證和會話信息管理。

通過“Kontrol”這個服務發現機制,一個Kite可以發現其他Kites與他們安全地進行身份驗證通信。Kontrol使用etcd作為后台儲存。但是你也用其他的替代(當前支持PostgreSQL),只要它實現了 kontrol.Storage接口。Kontrol同時也有許多認證用戶的方式。這是可定制的所以人們能用自己方式使用Kontrol。

如何使用Kite

我們現在來學習一下。編寫Kite並讓他們之間通信很有趣。首先,介紹一個最簡單的形式(原諒我忽略了錯誤處理,你不應該像我這樣:))

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域:

package main

import "github.com/koding/kite"

func main() {
	k := kite.New("first", "1.0.0")
	k.Config.Port = 6000
	k.Run()
}

如有需要,配置值也可以被環境變量覆蓋。

讓我們創建第二個kite來和第一個kite通信:

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方法。我們沒有傳遞任何參數(下面將解釋),所以如果你運行,可以看到:

$ go run second.go
pong

為Kite添加方法

現在我們添加第一個自定義方法。這個方法用來接收一個值並返回它的平方值。方法名字是square。要將函數分配給方法,只需確保其滿足 kite.Handler接口kite.Handler :

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 調用:

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 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地址:

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算法進行重連嘗試。因為Koding在實際生產也大量是用它,所以有許多類似這樣的小細節小改進。另一點是注冊時你不需要傳遞Kontrol的URL,因為你已經通過驗證,Kontrol的URL被存放在kite.key中了。你要做的僅僅是調用Register()方法。

現在我們尋找first kite並調用其square方法:

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)並隨意使用。歡迎貢獻!讓我知道你的想法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM