Kratos 的简介
Kratos 一套轻量级 Go 微服务框架,包含大量微服务相关框架及工具。
原则
- 简单:不过度设计,代码平实简单;
- 通用:通用业务开发所需要的基础库的功能;
- 高效:提高业务迭代的效率;
- 稳定:基础库可测试性高,覆盖率高,有线上实践安全可靠;
- 健壮:通过良好的基础库设计,减少错用;
- 高性能:性能高,但不特定为了性能做 hack 优化,引入 unsafe ;
- 扩展性:良好的接口设计,来扩展实现,或者通过新增基础库目录来扩展功能;
- 容错性:为失败设计,大量引入对 SRE 的理解,鲁棒性高;
- 工具链:包含大量工具链,比如 cache 代码生成,lint 工具等等;
特性
- 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:实现统一注册中心接口,可插件化对接各种注册中心;
架构
项目结构
我们创建了 kratos-layout 作为使用 kratos new
新建项目时所使用结构,其中包括了开发过程中所需的配套工具链( Makefile 等),便于开发者更高效地维护整个项目,本项目亦可作为使用 Kratos 构建微服务的工程化最佳实践的参考。
使用如下命令即可基于 kratos-layout 创建项目:
kratos new <project-name>
生成的目录结构如下:
.
├── 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
api
从目录结构看到,里面包含了三类文件:
*.proto
原始IDL文件*.go
利用protoc工具生成的go文件,包括http和grpc的服务相关代码*.swagger.json
利用工具生成的swagger接口文档
这部分的实现全是基于开源的protobuf
解决方案,对开发者很友好。这里提一个点:尽可能地用目录区分原始IDL文件与生成的文件。我简单列举两个优点:
- 让开发者更聚焦于原始IDL文件 - 其余文件均是从proto文件自动生成出来的,不应过多关注。
- 有利于IDL文件的传播 - proto文件可以快速生成其余语言的代码,独立文件夹更有利于扩散给外部调用者。
cmd
cmd简单来说就是main函数入口。
internal/biz
internal目录是go语言的一个特性,内部代码不会暴露给外部。
biz被理解为业务逻辑的组装层,如果能正确地理解这个概念,就能把握整个框架的分层设计了。我们从两个关键词来理解这个biz目录的设计:
- 业务逻辑 - 业务逻辑包括但不限于单个对象的增删改查,会处理很多进阶的内容,例如:
- 复合对象操作,如操作对象A后,再操作对象B
- 特殊逻辑,如创建A对象失败时,等待10s后再创建
- 并发策略,如并发访问对象A和对象B
- 组装层 - 重点在于组装底层基础的代码,如CRUD,而不是在biz层直接去操作数据库等
整体来说,biz这一层应重点考虑业务逻辑的信息密度,让业务开发者的重点放在这一层,把基础实现往下沉。是系统的核心层,所有具体的业务逻辑处理、事件处理等都在这层域模型中处理
internal/data
data被理解为缓存与数据库的封装,与底层数据存储相关,一般都是跟着数据库的类型适配。
internal/server
前面IDL文件(protobuf)生成了RPC方法的接口(interface),这里就是RPC方法的具体实现。
internal/service
service被定义成对数据结构的处理层。