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

原文連接:https://blog.gopheracademy.com/birthday-bash-2014/kite-microservice-library/ 此爲中文翻譯node

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

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

因此說搭建一個有許多應用的分佈式系統比較難。KodingKite庫旨在以一種簡單快捷輕便的方式搭建分佈式微服務應用。Kite框架自己有不少細節部分,在這篇文章中只會大概闡述Kite能幹什麼。web

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就是經過他們來讓他人鑑別和搜索本身。sql

Kite使用SockJS在不少不一樣傳輸方法(websocket, xhr, etc..)提供WebSocket模擬(emulation ),這意味着你也能夠經過瀏覽器來連接Kite(見Kite.js)。Kite使用修改過的dnode protocal來進行RPC消息傳遞。Kite協議增長了一個額外的session和authentication層,這樣就能輕鬆地識別Kite。在後臺,它使用JWT進行身份驗證和會話信息管理。shell

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

相關文章
相關標籤/搜索