文章目錄
- 1.Kratos 簡介
- 2.傳輸協議
- 3.日志
- 4.錯誤處理
- 5.配置管理
- 6.wire
1.Kratos 簡介
Kratos并不綁定于特定的基礎設施,不限定于某種注冊中心,或數據庫ORM等,所以您可以十分輕松地將任意庫集成進項目里,與Kratos共同運作。
API -> Service(wire) -> DB
- 可以看到kratos將整個服務大體分為了3層,API / Service / DB。
- 左側標注了在 Service和DB層,使用依賴注入(DI)進行實現,工具名稱為Wire。
- 可以看到Wire這個工具幾乎貫穿Kratos架構始終,是一個大角色。
2.傳輸協議
支持http + grpc兩種調用方式,通過編寫proto文件來實現。
一般,http開放給外部調用,可以使用restful風格定義。grpc面向內部微服務之間進行調用。
? 在項目中,會以這樣的結構出現,并且可以對不同協議進來的請求進行處理,添加處理的中間件,如權限校驗、熔斷限流等等。
3.日志
在kratos中,可以自定義日志框架選型,設置日志格式和輸出內容,然后將logger對象以依賴注入的方式,分配給server中的grpc server和http server,這樣就可以實現每次收到請求后的日志打印。
將logger對象以依賴注入的方式,注入到業務層,就可以在業務層中統一使用logger進行輸出。
4.錯誤處理
在grpc中,比較通用的一種錯誤處理方式就是直接通過 proto 預定義定義錯誤碼,然后通過 proto-gen-go 生成幫助代碼,直接返回 error。
{// 錯誤碼,跟 http-status 一致,并且在 grpc 中可以轉換成 grpc-status"code": 500,// 錯誤原因,定義為業務判定錯誤碼"reason": "USER_NOT_FOUND",// 錯誤信息,為用戶可讀的信息,可作為用戶提示內容"message": "invalid argument error",// 錯誤元信息,為錯誤添加附加可擴展信息"metadata": {"foo": "bar"}
}
這里可以發現,為了兼容grpc,在http的返回結果中,code也無法自定義,只能跟隨httpcode。所以這里客戶端或者第三方去處理錯誤時,需要判斷reason字段。
5.配置管理
使用proto文件定義配置和生成struct,然后將yaml中的內容讀取到對應struct 字段中進行使用。
在這里我們可以注意到,在kratos中,除了傳輸格式使用了proto進行定義之外,錯誤處理和配置管理,也使用了proto來進行。可以說,一切皆proto。
6.wire
Wire 是一個靈活的依賴注入工具(需要安裝),通過自動生成代碼的方式在編譯期完成依賴注入。通過 Wire 進行初始化代碼,可以很好地解決組件之間的耦合,以及提高代碼維護性。
打開Kratos的示例項目,從main入口看,有一處調用了wireApp方法,這里就是一切的源頭(萬惡之源)。
這個方法調用的是main同目錄的wire文件中的wireApp方法,同目錄的wire_gen.go實現了此方法。
wire_gen中去實例化不同service和組建的對象,用于調用。關系圖如下:
server -> service -> biz -> data
main.go -> wire.go(wire_gen.go)
wire.go 中有用到ProviderSet
package mainimport ("kratos-demo03/internal/biz""kratos-demo03/internal/conf""kratos-demo03/internal/data""kratos-demo03/internal/server""kratos-demo03/internal/service""github.com/go-kratos/kratos/v2""github.com/go-kratos/kratos/v2/log""github.com/google/wire"
)// wireApp init kratos application.
func wireApp(*conf.Server, *conf.Data, log.Logger) (*kratos.App, func(), error) {panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp))
}
main.go 有用到App和Config
package mainimport ("flag""os""kratos-demo03/internal/conf""github.com/go-kratos/kratos/v2""github.com/go-kratos/kratos/v2/config""github.com/go-kratos/kratos/v2/config/file""github.com/go-kratos/kratos/v2/log""github.com/go-kratos/kratos/v2/middleware/tracing""github.com/go-kratos/kratos/v2/transport/grpc""github.com/go-kratos/kratos/v2/transport/http"_ "go.uber.org/automaxprocs"
)// go build -ldflags "-X main.Version=x.y.z"
var (// Name is the name of the compiled software.Name string// Version is the version of the compiled software.Version string// flagconf is the config flag.flagconf stringid, _ = os.Hostname()
)func init() {flag.StringVar(&flagconf, "conf", "../../configs", "config path, eg: -conf config.yaml")
}func newApp(logger log.Logger, gs *grpc.Server, hs *http.Server) *kratos.App {return kratos.New(kratos.ID(id),kratos.Name(Name),kratos.Version(Version),kratos.Metadata(map[string]string{}),kratos.Logger(logger),kratos.Server(gs,hs,),)
}func main() {flag.Parse()logger := log.With(log.NewStdLogger(os.Stdout),"ts", log.DefaultTimestamp,"caller", log.DefaultCaller,"service.id", id,"service.name", Name,"service.version", Version,"trace.id", tracing.TraceID(),"span.id", tracing.SpanID(),)c := config.New(config.WithSource(file.NewSource(flagconf),),)defer c.Close()if err := c.Load(); err != nil {panic(err)}var bc conf.Bootstrapif err := c.Scan(&bc); err != nil {panic(err)}app, cleanup, err := wireApp(bc.Server, bc.Data, logger)if err != nil {panic(err)}defer cleanup()// start and wait for stop signalif err := app.Run(); err != nil {panic(err)}
}
在每個模塊中,只需要一個 ProviderSet 提供者集合,就可以在 wire 中進行依賴注入。
有一個數據庫連接對象,service需要操作數據庫,依賴數據庫連接對象。這時候我們可以聲明數據庫連接對象在ProviderSet集合,然后在service對象處聲明,我需要一個數據庫連接對象。 然后我們使用wire工具,就可以自動幫我們生成依賴注入的代碼。
這里的依賴注入讓代碼間的依賴關系一目了然。只需要查看wire_gen.go代碼就可以了解依賴關系。