您好,地球人,歡迎來到Kratos漫游指南。
對於剛開始研究Kratos框架的開發者來說,目前的文檔有些零散,這與我們的模塊化設計有一些關系,不過Don't panic,從這篇文章開始,我將試圖打破這一現狀,漫游指南系列將循序漸進地介紹Kratos框架,理順框架的使用思路,使您更快上手Kratos。
同時,這個系列也會逐步整合進官方文檔中,同時重新組織整個文檔的結構和內容,敬請期待。
本篇是該系列的第一篇,主要介紹Kratos的整體概況。
設計哲學
Kratos是一個Go語言實現的微服務框架,說得更准確一點,它更類似於一個使用Go構建微服務的工具箱,開發者可以按照自己的習慣選用或定制其中的的組件,來打造自己的微服務。也正是由於這樣的原因,Kratos並不綁定於特定的基礎設施,不限定於某種注冊中心,或數據庫ORM等,所以您可以十分輕松地將任意庫集成進項目里,與Kratos共同運作。
圍繞這樣的核心設計理念,我們設計了如下的項目生態:
- kratos Kratos框架核心,主要包含了基礎的CLI工具,內置的HTTP/gRPC接口生成和服務生命周期管理,提供鏈路追蹤、配置文件、日志、服務發現、監控等組件能力和相關接口定義。
- contrib 基於上述核心定義的基礎接口,對配置文件、日志、服務發現、監控等服務進行具體實現所形成的一系列插件,可以直接使用它們,也可以參考它們的代碼,做您需要的服務的適配,從而集成進kratos項目中來。
- aegis 我們將服務可用性相關的算法:如限流、熔斷等算法放在了這個獨立的項目里,幾乎沒有外部依賴,它更不依賴Kratos,您可以在直接在任意項目中使用。您也可以輕松將它集成到Kratos中使用,提高服務的可用性。
- layout 我們設計的一個默認的項目模板,它包含一個參考了DDD和簡潔架構設計的項目結構、Makefile腳本和Dockerfile文件。但這個項目模板不是必需的,您可以任意修改它,或使用自己設計的項目結構,Kratos依然可以正常工作。框架本身不對項目結構做任何假設和限制,您可以按照自己的想法來使用,具有很強的可定制性。
- gateway 這個是我們剛剛起步,用Go開發的API Gateway,后續您可以使用它來作為您Kratos微服務的網關,用於微服務API的治理,項目正在施工中,歡迎關注。
倉庫、文檔和社區
GitHub倉庫:https://github.com/go-kratos
文檔:https://go-kratos.dev/
微信群:go-kratos 官方微信群
Discord:go-kratos
為什么v2完全重新設計
以前關注過kratos項目的可能知道,Kratos的v1版本已經開源了很久,也是個較為完善的框架。那么為什么不直接基於v1繼續迭代,而是要推倒重來,推出完全重新設計的v2呢?
經驗源自踩坑。
在業務不斷迭代、項目不斷膨脹的情況下,我們發現,過去的框架和項目結構設計,導致代碼變更成本逐漸升高,而沒有進行合理的抽象,導致更難進行模塊的測試,也更難對第三方基礎庫進行適配和遷移,這在一定程度上拉低了生產力。
因此,我們參考了大量的DDD和Clean Architecture等業界先進設計理念,重新設計了微服務的項目結構,並且這個結構隨着我們的后續研究,會進一步進行迭代,讓它成為微服務項目結構的最佳實踐。
沒錯,新版本的是從kratos-layout開始的。也許剛接觸這個項目結構時會覺得不適應,但隨着項目迭代,代碼復雜度的提高,這個定義良好的結構,將使項目保持優秀的代碼可讀性、可測試性,以及令人滿意的開發效率和可維護性。
更重要的一點是,這一次我們想面向社區來設計和開發這個框架。讓更多的開發者能夠使用我們的框架來提高生產力,同時參與到我們的項目中來。
所以我們把整個框架設計成為一個插座,我們希望整個框架輕量,插件化,可定制。對於幾乎每一個微服務相關的功能模塊,我們都設計了標准化接口,對於第三方庫設計為插件,這樣就能迅速把任意基礎設施集成到使用Kratos的項目里,因此,無論您的公司使用何種基礎設施,有何種規范,您都可以輕松將Kratos定制成與您的開發、生產環境相匹配的樣子。
不破不立,v2是一次從內到外的徹底革新,我們無法在舊版本上修修補補,而是選擇重新設計和開發新版本。而目前v2版本也已經在很多生產環境使用,我們也將持續迭代和完善這個框架,同時也更歡迎各位開發者參與進來,一起讓它變得更好。
數據庫/緩存/消息隊列/...
正如前文提到的,Kratos框架不限制您使用任何第三方庫來進行項目開發,因此您可以根據喜好來選擇庫進行集成。我們也會逐步針對更多被廣泛使用的第三方庫開發插件。
這里給出一些被廣泛使用的庫供參考:
數據庫:
- database/sql 官方庫
- gorm
- ent
緩存:
消息隊列:
其它更多的優秀go庫,可以在awesome-go這個倉庫中找找。
CLI工具
kratos命令目前主要用於從模板創建項目,維護依賴包版本等。具體請參考文檔
Protobuf定義API
Kratos使用Protobuf進行API定義。Protobuf是由Google開發的一種語言中立的數據序列化協議。它有結構定義清晰、可擴展性好、體積小、性能優秀等特點,在眾多公司和項目被廣泛使用。
在使用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來定義您的字段。可以參考我們的upload例子進行實現。
元信息傳遞
服務之間的API調用,如果有某些元信息需要傳遞過去,而不是寫在payload消息中,可以使用Metadata包進行字段設置和提取,具體細節參考元信息傳遞文檔
錯誤處理
Kratos的errors模塊提供了error的封裝。框架也預定義了一系列標准錯誤供使用。
錯誤處理這一塊的設計也經過了很久的討論才定下來,主要設計理念如下:
code
語義近似HTTP的Status Code(例如客戶端傳參數錯誤用400)同時也作為大類錯誤,在HTTP接口中的HTTP Code會使用它,好處是網關層可以根據這個code觸發相應策略(重試、限流、熔斷等)。reason
業務的具體錯誤碼,為可讀的字符串,能夠表明,在同一個服務中應該唯一。message
用戶可讀的信息,可以在客戶端(App、瀏覽器等)進行相應的展示給用戶看。metadata
為一些附加信息,可以作為補充信息使用。
在API返回的錯誤信息中,以HTTP接口為例,消息結構大概是長這個樣子的:
{
// 錯誤碼,跟 http-status 一致,並且在 grpc 中可以轉換成 grpc-status
"code": 500,
// 錯誤原因,定義為業務判定錯誤碼
"reason": "USER_NOT_FOUND",
// 錯誤信息,為用戶可讀的信息,可作為用戶提示內容
"message": "invalid argument error",
// 錯誤元信息,為錯誤添加附加可擴展信息
"metadata": {"some-key": "some-value"}
}
在Kratos中您可以使用proto文件定義您的業務錯誤,並通過工具生成對應的處理邏輯和方法。(如使用layout中提供的make errors
指令。)
錯誤定義:
syntax = "proto3";
package api.blog.v1;
import "errors/errors.proto";
option go_package = "github.com/go-kratos/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];;
}
錯誤創建:
// 通過 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提供了統一的接口,支持配置文件的加載和變更訂閱。
通過實現Source 和 Watcher即可實現任意配置源(本地或遠程)的配置文件加載和變更訂閱。
已經實現了下列插件:
- file 本地文件加載,Kratos內置
- apollo
- etcd
- kubernetes
- nacos
服務注冊&服務發現
Kratos定義了統一的注冊接口,通過實現Registrar和Discovery,您可以很輕松地將Kratos接入到您的注冊中心中。
您也可以直接使用我們已經實現好的插件:
日志
Kratos的日志模塊由兩部分組成:
- Logger:底層日志接口,用於快速適配各種日志庫到框架中來,僅提供一個最簡單的Log方法。
- Helper:高級日志接口,提供了一系列帶有日志等級和格式化方法的幫助函數,通常業務邏輯中建議使用這個,能夠簡化日志代碼。
我們已經實現好的插件用於適配目前一些日志庫,您也可以參考它們的代碼來實現自己需要的日志庫的適配:
監控
監控告警方面,您可以通過實現metrics相關接口將服務的統計數據上報給監控平台。
也可以直接使用我們已經實現好的插件:
鏈路追蹤
Kratos使用OpenTelemetry作為分布式鏈路追蹤所使用的標准,您可以通過對client和server配置tracing來將服務接入到鏈路追蹤平台(如jaeger等),從而對服務的接口調用關系,耗時,錯誤等進行追蹤。
負載均衡
Kratos內置了若干種負載均衡算法,如Weighted round robin(默認)、P2C,Random等,您可以通過在client初始化時配置來使用他們。
限流熔斷
Kratos提供了限流ratelimit和熔斷circuitbreaker中間件,用於微服務出現異常故障時自動對流量進行限制,提升服務的健壯性,避免雪崩。這兩個中間件使用的算法,也可以在我們的可用性算法倉庫aegis中找到,獨立於Kratos直接使用。
中間件
您可以通過Kratos的middleware機制,統一微服務接口的某些共同邏輯。上面提到的功能插件,您可以通過實現Middleware編寫Kratos能夠使用的中間件。
同時在倉庫的middleware目錄下,我們也提供了一系列中間件供您使用。
插件
除了上述提到的插件外,我們還提供了一些其它插件,完整的插件列表請參考文檔社區插件
示例代碼
如果您看過文檔后,對某些功能的使用仍有疑惑,或者是希望尋找一些用Kratos寫項目的靈感,在倉庫的examples目錄下我們提供了很多代碼供參考。
您也可以通過文檔中的示例代碼清單頁面來查閱有哪些示例。
小結
使用Kratos在一般開發過程中用到大部分常用功能點,在本文已經做了簡單介紹,相信您對本框架的情況已經有了大致的了解。限於篇幅原因,無法在一篇文章中涵蓋到Kratos的全部細節,比如layout這個相對復雜的項目結構具體是怎么用的,我將在后續的文章中,繼續對Kratos的框架設計思想和使用方法做更加詳細的介紹,並且會結合具體的項目實例,介紹使用Kratos開發的完整流程。