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)并随意使用。欢迎贡献!让我知道你的想法。

相关文章
相关标签/搜索