Kratos 微服务框架
1.简介
B站基于Golang实现的一个轻量级开源的面向微服务的框架.
Kratos框架不限制您使用任何第三方库来进行项目开发,因此您可以根据喜好来选择库进行集成。我们也会逐步针对更多被广泛使用的第三方库开发插件。
2.官方文档
https://go-kratos.dev/docs/
3.架构图
特性:
- APIs:协议通信以 HTTP/gRPC 为基础,通过 Protobuf 进行定义;
- Errors:通过 Protobuf 的 Enum 作为错误码定义,以及工具生成判定接口;
- Metadata:在协议通信 HTTP/gRPC 中,通过 Middleware 规范化服务元信息传递;
- Config:支持多数据源方式,进行配置合并铺平,通过 Atomic 方式支持动态配置;
- Logger:标准日志接口,可方便集成三方 log 库,并可通过 fluentd 收集日志;
- Metrics:统一指标接口,可以实现各种指标系统,默认集成 Prometheus;
- Tracing:遵循 OpenTelemetry 规范定义,以实现微服务链路追踪;
- Encoding:支持 Accept 和 Content-Type 进行自动选择内容编码;
- Transport:通用的 HTTP/gRPC 传输层,实现统一的 Middleware 插件支持;
- Registry:实现统一注册中心接口,可插件化对接各种注册中心;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i17ntljO-1671006695145)(image/Kratos微服务框架/1670998161976.png)]
4. 广泛使用的库
kratos 是一个 轻量级插件化的微服务框架. 使用者插入第三方库使用,灵活度更大
数据库:
- database/sql 官方库
- gorm
- ent
缓存:
- go-redis
- redigo
- gomemcache
消息队列:
- sarama kafka客户端
- kafka-go
5 CLI工具
kratos命令目前主要用于从模板创建项目,维护依赖包版本等。具体请参考文档
6. Protobuf定义API
Kratos使用Protobuf进行API定义. 在使用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 greetingrpc 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 greetingsmessage HelloReply {string message = 1;}
7. 元信息传递
服务之间的API调用,如果有某些元信息需要传递过去,而不是写在payload消息中,可以使用Metadata包进行字段设置和提取,具体细节参考元信息传递文档
8. 错误处理
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/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 nameapi.ErrorUserNotFound("user %s not found", "kratos")// 传递metadataerr := 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.Codee := errors.FromError(err)ife.Reason == "USER_NAME_EMPTY" && e.Code == 500 {// do something}// 通过 proto 生成的代码断言错误,并且包名应替换为自己生成代码后的 package nameif api.IsUserNotFound(err) {// do something})
9.配置文件
Kratos提供了统一的接口,支持配置文件的加载和变更订阅。
通过实现Source 和 Watcher即可实现任意配置源(本地或远程)的配置文件加载和变更订阅。
已经实现了下列插件:
- file 本地文件加载,Kratos内置
- apollo
- etcd
- kubernetes
- nacos
10. 服务注册&服务发现
Kratos定义了统一的注册接口,通过实现Registrar和Discovery,您可以很轻松地将Kratos接入到您的注册中心中。
您也可以直接使用我们已经实现好的插件:
- consul
- discovery
- etcd
- kubernetes
- nacos
- zookeeper
11.日志
Kratos的日志模块由两部分组成:
- Logger:底层日志接口,用于快速适配各种日志库到框架中来,仅提供一个最简单的Log方法。
- Helper:高级日志接口,提供了一系列带有日志等级和格式化方法的帮助函数,通常业务逻辑中建议使用这个,能够简化日志代码。
我们已经实现好的插件用于适配目前一些日志库,您也可以参考它们的代码来实现自己需要的日志库的适配:
- std 标准输出,Kratos内置
- fluent
- zap
12. 监控
监控告警方面,您可以通过实现metrics相关接口将服务的统计数据上报给监控平台。
也可以直接使用我们已经实现好的插件:
- datadog
- prometheus
13. 链路追踪
Kratos使用OpenTelemetry作为分布式链路追踪所使用的标准,您可以通过对client和server配置tracing来将服务接入到链路追踪平台(如jaeger等),从而对服务的接口调用关系,耗时,错误等进行追踪。
14. 负载均衡
Kratos内置了若干种负载均衡算法,如Weighted round robin(默认)、P2C,Random等,您可以通过在client初始化时配置来使用他们。
15.限流熔断
Kratos提供了限流ratelimit和熔断circuitbreaker中间件,用于微服务出现异常故障时自动对流量进行限制,提升服务的健壮性,避免雪崩。
这两个中间件使用的算法,也可以在我们的可用性算法仓库aegis中找到,独立于Kratos直接使用。
16. 中间件
您可以通过Kratos的middleware机制,统一微服务接口的某些共同逻辑。上面提到的功能插件,您可以通过实现Middleware编写Kratos能够使用的中间件。
同时在仓库的middleware目录下,我们也提供了一系列中间件供您使用。
17. 插件
除了上述提到的插件外,我们还提供了一些其它插件,完整的插件列表请参考文档社区插件
18.示例代码
如果您看过文档后,对某些功能的使用仍有疑惑,或者是希望寻找一些用Kratos写项目的灵感,在examples仓库的目录下我们提供了很多代码供参考。
您也可以通过文档中的示例代码清单页面来查阅有哪些示例。
19.项目结构
我们创建了 kratos-layout 作为使用 kratos new
新建项目时所使用结构,其中包括了开发过程中所需的配套工具链( Makefile 等),便于开发者更高效地维护整个项目,本项目亦可作为使用 Kratos 构建微服务的工程化最佳实践的参考。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MamWmiNH-1671006695146)(image/Kratos微服务框架/1671002891484.png)]
20. 使用如下命令即可基于 kratos-layout 创建项目:
kratos new
生成的目录结构如下:
.
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── api // 下面维护了微服务使用的proto文件以及根据它们所生成的go文件
│ └── helloworld
│ └── v1
│ ├── error_reason.pb.go
│ ├── error_reason.proto
│ ├── error_reason.swagger.json
│ ├── greeter.pb.go
│ ├── greeter.proto
│ ├── greeter.swagger.json
│ ├── greeter_grpc.pb.go
│ └── greeter_http.pb.go
├── cmd // 整个项目启动的入口文件
│ └── server
│ ├── main.go
│ ├── wire.go // 我们使用wire来维护依赖注入
│ └── wire_gen.go
├── configs // 这里通常维护一些本地调试用的样例配置文件
│ └── config.yaml
├── generate.go
├── go.mod
├── go.sum
├── internal // 该服务所有不对外暴露的代码,通常的业务逻辑都在这下面,使用internal避免错误引用
│ ├── biz // 业务逻辑的组装层,类似 DDD 的 domain 层,data 类似 DDD 的 repo,而 repo 接口在这里定义,使用依赖倒置的原则。
│ │ ├── README.md
│ │ ├── biz.go
│ │ └── greeter.go
│ ├── conf // 内部使用的config的结构定义,使用proto格式生成
│ │ ├── conf.pb.go
│ │ └── conf.proto
│ ├── data // 业务数据访问,包含 cache、db 等封装,实现了 biz 的 repo 接口。我们可能会把 data 与 dao 混淆在一起,data 偏重业务的含义,它所要做的是将领域对象重新拿出来,我们去掉了 DDD 的 infra层。
│ │ ├── README.md
│ │ ├── data.go
│ │ └── greeter.go
│ ├── server // http和grpc实例的创建和配置
│ │ ├── grpc.go
│ │ ├── http.go
│ │ └── server.go
│ └── service // 实现了 api 定义的服务层,类似 DDD 的 application 层,处理 DTO 到 biz 领域实体的转换(DTO -> DO),同时协同各类 biz 交互,但是不应处理复杂逻辑
│ ├── README.md
│ ├── greeter.go
│ └── service.go
└── third_party // api 依赖的第三方proto
├── README.md
├── google
│ └── api
│ ├── annotations.proto
│ ├── http.proto
│ └── httpbody.proto
└── validate
├── README.md
└── validate.proto
21.推荐阅读
- Go 工程化 – Project Layout 最佳实践
- Kratos 学习笔记 – 通过 layout 简单分析应用是如何跑起来的