本篇文檔闡述Kratos的設計理念,介紹Kratos項目的整體情況和主要組件
Kratos是一個Go語言實現的微服務框架,說得更準確一點,它更類似于一個使用Go構建微服務的工具箱,開發(fā)者可以按照自己的習慣選用或定制其中的的組件,來打造自己的微服務。也正是由于這樣的原因,Kratos并不綁定于特定的基礎設施,不限定于某種注冊中心,或數據庫ORM等,所以您可以十分輕松地將任意庫集成進項目里,與Kratos共同運作。
圍繞這樣的核心設計理念,我們設計了如下的項目生態(tài):
kratos
?Kratos框架核心,主要包含了基礎的CLI工具,內置的HTTP/gRPC接口生成和服務生命周期管理,提供鏈路追蹤、配置文件、日志、服務發(fā)現、監(jiān)控等組件能力和相關接口定義。
contrib
?基于上述核心定義的基礎接口,對配置文件、日志、服務發(fā)現、監(jiān)控等服務進行具體實現所形成的一系列插件,可以直接使用它們,也可以參考它們的代碼,做您需要的服務的適配,從而集成進kratos項目中來。
aegis
?我們將服務可用性相關的算法:如限流、熔斷等算法放在了這個獨立的項目里,幾乎沒有外部依賴,它更不依賴Kratos,您可以在直接在任意項目中使用。您也可以輕松將它集成到Kratos中使用,提高服務的可用性。
layout
?我們設計的一個默認的項目模板,它包含一個參考了DDD和簡潔架構設計的項目結構、Makefile腳本和Dockerfile文件。但這個項目模板不是必需的,您可以任意修改它,或使用自己設計的項目結構,Kratos依然可以正常工作??蚣鼙旧聿粚椖拷Y構做任何假設和限制,您可以按照自己的想法來使用,具有很強的可定制性。
gateway
?這個是我們剛剛起步,用Go開發(fā)的API Gateway,后續(xù)您可以使用它來作為您Kratos微服務的網關,用于微服務API的治理,項目正在施工中,歡迎關注。GitHub倉庫:https://github.com/go-kratos
微信群:go-kratos 官方微信群
Discord:go-kratos
以前關注過kratos項目的可能知道,Kratos的v1版本已經開源了很久,也是個較為完善的框架。那么為什么不直接基于v1繼續(xù)迭代,而是要推倒重來,推出完全重新設計的v2呢?
經驗源自踩坑。
在業(yè)務不斷迭代、項目不斷膨脹的情況下,我們發(fā)現,過去的框架和項目結構設計,導致代碼變更成本逐漸升高,而沒有進行合理的抽象,導致更難進行模塊的測試,也更難對第三方基礎庫進行適配和遷移,這在一定程度上拉低了生產力。
因此,我們參考了大量的DDD和Clean Architecture等業(yè)界先進設計理念,重新設計了微服務的項目結構,并且這個結構隨著我們的后續(xù)研究,會進一步進行迭代,讓它成為微服務項目結構的最佳實踐。
沒錯,新版本的是從kratos-layout開始的。也許剛接觸這個項目結構時會覺得不適應,但隨著項目迭代,代碼復雜度的提高,這個定義良好的結構,將使項目保持優(yōu)秀的代碼可讀性、可測試性,以及令人滿意的開發(fā)效率和可維護性。
更重要的一點是,這一次我們想面向社區(qū)來設計和開發(fā)這個框架。讓更多的開發(fā)者能夠使用我們的框架來提高生產力,同時參與到我們的項目中來。
所以我們把整個框架設計成為一個插座,我們希望整個框架輕量,插件化,可定制。對于幾乎每一個微服務相關的功能模塊,我們都設計了標準化接口,對于第三方庫設計為插件,這樣就能迅速把任意基礎設施集成到使用Kratos的項目里,因此,無論您的公司使用何種基礎設施,有何種規(guī)范,您都可以輕松將Kratos定制成與您的開發(fā)、生產環(huán)境相匹配的樣子。
不破不立,v2是一次從內到外的徹底革新,我們無法在舊版本上修修補補,而是選擇重新設計和開發(fā)新版本。而目前v2版本也已經在很多生產環(huán)境使用,我們也將持續(xù)迭代和完善這個框架,同時也更歡迎各位開發(fā)者參與進來,一起讓它變得更好。
正如前文提到的,Kratos框架不限制您使用任何第三方庫來進行項目開發(fā),因此您可以根據喜好來選擇庫進行集成。我們也會逐步針對更多被廣泛使用的第三方庫開發(fā)插件。
這里給出一些被廣泛使用的庫供參考:
數據庫:
緩存:
消息隊列:
其它更多的優(yōu)秀go庫,可以在awesome-go這個倉庫中找找。
kratos命令目前主要用于從模板創(chuàng)建項目,維護依賴包版本等。
Kratos使用Protobuf進行API定義。Protobuf是由Google開發(fā)的一種語言中立的數據序列化協議。它有結構定義清晰、可擴展性好、體積小、性能優(yōu)秀等特點,在眾多公司和項目被廣泛使用。
在使用Kratos的項目中,您將使用如下的IDL進行您的接口定義,并且通過protoc工具生成相應的.pb.go文件,其中包含根據定義生成的的服務端和客戶端代碼。隨后您就可以在自己的項目內部注冊服務端代碼使用,或引用客戶端代碼進行遠程調用。
Kratos默認僅生成gRPC接口的代碼,如果需要生成HTTP代碼,請在proto文件中使用option (google.api.http)來添加HTTP部分的定義后再進行生成。默認情況下,HTTP接口將使用JSON作為序列化格式,如果想使用其它序列化格式(form,XML等),請參考文檔序列化進行相應的配置即可。
syntax = "proto3";
package helloworld.v1;
import "google/api/annotations.proto";
option go_package = "github.com/go-kratos/kratos-layout/api/helloworld/v1;v1";
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
get: "/helloworld/{name}"
};
}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
需要注意,雖然Protobuf定義的API的可靠性更強,但字段結構靈活性相對JSON要弱一些,因此如果您有諸如文件上傳接口,或者某些無法對應到proto的JSON結構需要使用,我門還提供了“逃生門”,在我們的Protobuf體系之外定義這些接口,實現為普通的http.Handler并且掛載到路由上,或者用struct來定義您的字段??梢詤⒖嘉覀兊?a href="http://o2fo.com/targetlink?url=https://github.com/go-kratos/examples/blob/main/http/upload/main.go" target="_blank">upload例子進行實現。
服務之間的API調用,如果有某些元信息需要傳遞過去,而不是寫在payload消息中,可以使用Metadata包進行字段設置和提取。
Kratos的errors模塊提供了error的封裝??蚣芤差A定義了一系列標準錯誤供使用。
錯誤處理這一塊的設計也經過了很久的討論才定下來,主要設計理念如下:
code
?語義近似HTTP的Status Code(例如客戶端傳參數錯誤用400)同時也作為大類錯誤,在HTTP接口中的HTTP Code會使用它,好處是網關層可以根據這個code觸發(fā)相應策略(重試、限流、熔斷等)。
reason
?業(yè)務的具體錯誤碼,為可讀的字符串,能夠表明,在同一個服務中應該唯一。
message
?用戶可讀的信息,可以在客戶端(App、瀏覽器等)進行相應的展示給用戶看。
metadata
?為一些附加信息,可以作為補充信息使用。在API返回的錯誤信息中,以HTTP接口為例,消息結構大概是長這個樣子的:
{
// 錯誤碼,跟 http-status 一致,并且在 grpc 中可以轉換成 grpc-status
"code": 500,
// 錯誤原因,定義為業(yè)務判定錯誤碼
"reason": "USER_NOT_FOUND",
// 錯誤信息,為用戶可讀的信息,可作為用戶提示內容
"message": "invalid argument error",
// 錯誤元信息,為錯誤添加附加可擴展信息
"metadata": {"some-key": "some-value"}
}
在Kratos中您可以使用proto文件定義您的業(yè)務錯誤,并通過工具生成對應的處理邏輯和方法。(如使用layout中提供的make errors指令。)
錯誤定義:
syntax = "proto3";
package api.blog.v1;
import "errors/errors.proto";
option go_package = "github.com/go-kratos/examples/blog/api/v1;v1";
enum ErrorReason {
// 設置缺省錯誤碼
option (errors.default_code) = 500;
// 為某個枚舉單獨設置錯誤碼
USER_NOT_FOUND = 0 [(errors.code) = 404];
CONTENT_MISSING = 1 [(errors.code) = 400];;
}
錯誤創(chuàng)建:
// 通過 errors.New() 響應錯誤
errors.New(500, "USER_NAME_EMPTY", "user name is empty")
// 通過 proto 生成的代碼響應錯誤,并且包名應替換為自己生成代碼后的 package name
api.ErrorUserNotFound("user %s not found", "kratos")
// 傳遞metadata
err := errors.New(500, "USER_NAME_EMPTY", "user name is empty")
err = err.WithMetadata(map[string]string{
"foo": "bar",
})
錯誤斷言:
err := wrong()
// 通過 errors.Is() 斷言
if errors.Is(err,errors.BadRequest("USER_NAME_EMPTY","")) {
// do something
}
// 通過判斷 *Error.Reason 和 *Error.Code
e := errors.FromError(err)
if e.Reason == "USER_NAME_EMPTY" && e.Code == 500 {
// do something
}
// 通過 proto 生成的代碼斷言錯誤,并且包名應替換為自己生成代碼后的 package name
if api.IsUserNotFound(err) {
// do something
})
Kratos提供了統(tǒng)一的接口,支持配置文件的加載和變更訂閱。
通過實現Source 和 Watcher即可實現任意配置源(本地或遠程)的配置文件加載和變更訂閱。
已經實現了下列插件:
Kratos定義了統(tǒng)一的注冊接口,通過實現Registrar和Discovery,您可以很輕松地將Kratos接入到您的注冊中心中。
您也可以直接使用我們已經實現好的插件:
Kratos的日志模塊由兩部分組成:
我們已經實現好的插件用于適配目前一些日志庫,您也可以參考它們的代碼來實現自己需要的日志庫的適配:
監(jiān)控告警方面,您可以通過實現metrics相關接口將服務的統(tǒng)計數據上報給監(jiān)控平臺。
也可以直接使用我們已經實現好的插件:
Kratos使用OpenTelemetry作為分布式鏈路追蹤所使用的標準,您可以通過對client和server配置tracing來將服務接入到鏈路追蹤平臺(如jaeger等),從而對服務的接口調用關系,耗時,錯誤等進行追蹤。
Kratos內置了若干種負載均衡算法,如Weighted round robin(默認)、P2C,Random等,您可以通過在client初始化時配置來使用他們。
Kratos提供了限流ratelimit和熔斷circuitbreaker中間件,用于微服務出現異常故障時自動對流量進行限制,提升服務的健壯性,避免雪崩。這兩個中間件使用的算法,也可以在我們的可用性算法倉庫aegis中找到,獨立于Kratos直接使用。
您可以通過Kratos的middleware機制,統(tǒng)一微服務接口的某些共同邏輯。上面提到的功能插件,您可以通過實現Middleware編寫Kratos能夠使用的中間件。
同時在倉庫的middleware目錄下,我們也提供了一系列中間件供您使用。
除了上述提到的插件外,我們還提供了一些其它插件,完整的插件列表請參考文檔社區(qū)插件
如果您看過文檔后,對某些功能的使用仍有疑惑,或者是希望尋找一些用Kratos寫項目的靈感,在倉庫的examples目錄下我們提供了很多代碼供參考。
更多建議: