砥砺前行 | Kratos 框架 v2 版本架构演进之路

article/2025/10/5 14:15:11

Kratos 是一套轻量级 Go 微服务框架,包含大量微服务相关功能及工具。名字来源于游戏《战神》,该游戏以希腊神话为背景,讲述了奎托斯(Kratos)由凡人成为战神并展开弑神屠杀的冒险历程。

写在前面

从 2021 年 2 月份,github 上 kratos v2(下文简称 kratos)版本第一次代码提交,到功能模块的讨论,修改,测试,最终定稿,已经过去了 13 个月,在社区各位伙伴的贡献下,kratos v2 已经从 2.0.0 alpha1 版本迭代到了 2.2.1 版本,已经具备微服务框架的完整能力。在此感谢各位社区伙伴的贡献。

 

f7bdd35024d285e981a381f752bfc92e.png


概览

kratos v1(下文简称 v1) 版本在设计时,后期的可扩展性考虑较少,框架模块与实现强依赖,类似于全家桶,导致框架本身灵活性不高,框架使用者无法更换框架模块的具体实现,没有办法在不衍生下游版本的前提下对框架功能实现进行替换,在实际的企业开发中对于企业的多样化需求,无法轻松地应对,遇到这种需求时,只能通过修改框架代码来实现。而kratos v2 版本它更像是一个采用 Go 语言构建微服务的工具箱,开发者可以按照自己的习惯像搭积木一样来构建自己的微服务。也正是由于这样的原因,kratos v2 并不会直接绑定某种特定的基础设施,所以可以很轻松地将任意您想要的库集成到项目中,与 kratos v2 共同协作。

kratos v2 版本的设计思想就是支持高度自由的定制化,框架制定接口规范,然后通过插件来实现具体需求,实现高度可拔插的微服务框架,企业在开发时可以选择框架已经提供的插件实现,也可以自己定制插件,实现了高度的可定制。并且在 kratos v2 版本中 API定义gRPC ServiceHTTP Service请求参数校验错误定义Swagger API json应用配置模版等都是基于 Protobuf IDL 来构建的:

e4eadff8dcff01dc623ea26f5aa88f2f.png

项目生态

围绕着 kratos v2 版本的核心设计理念,设计了如下的项目生态:

  • kratos 框架核心,包含了基础的 CLI 工具,内置支持了 HTTP/gRPC 传输协议,提供了服务的完整声明周期管理,提供了如 API 、日志 、错误处理、 配置、监控、序列化、注册发现、元数据传递、传输层、中间件等组件能力和相关接口定义。

  • contrib 基于框架核心定义的基础接口,实现了对配置文件、日志系统、服务发现、监控等基础服务设施的适配,可以让开发者直接集成到 Kratos 项目中来。

  • aegis 服务可用性的相关算法如:限流、熔断等。算法放在了独立的项目中,几乎没有外部依赖,即使您的项目没有依赖 Kratos 框架,您也可以直接任意项目中使用它。

  • layout 参考了《领域驱动设计》和《简洁架构设计》的项目模板,并且提供了 Makefile 脚本和 Dockerfile 文件。我们推荐您使用 kratos 提供的项目结构,但您可以随意修改这个模板,或者使用自己喜欢的项目结构,框架本身不对项目做任何限制,您可以按照自己的想法来使用,具有很强的可定制性。

  • gateway 一个使用 Go 语言开发的 API Gateway,后续您可以使用它作为您项目的微服务网关,用于微服务 API 的治理,项目正在研发中,敬请期待。

架构设计

9e6d1cbb9cc32ce9602e225eb4fb5d2f.png

kratos v2 版本在设计阶段主要进行了以下几个方面的思考:

  • 面向包的设计理念

  • Transport HTTP/gRPC

  • 应用生命周期管理

  • 配置规范的思考

  • 业务错误的设计

  • 日志接口的设计

  • Metadata 传递和使用

  • Middleware 使用

  • 简化的 DDD layout 实现

面向包的设计理念

在 kratos v2 框架中,我们主要是参考了 Go 的基础库设计思想,包名按照实际功能划分,每个包都具有单一的职责,当用户不可见或者不稳定的接口放到了/internal 目录中。并且在框架中不同包具有不同的功能特性:

  • /cmd cmd 中包含了可以通过 go install 或 go get 一键安装的命令行工具,使用户可以更加方便的使用框架。

  • /errors 统一的业务错误封装,便捷的返回错误码以及具体的业务错误原因。

  • /config支持多数据源接入,可以对配置进行合并,平铺,通过 Atomic 方式支持配置热更新。

  • /transport传输层(HTTP/gRPC)的抽象封装。

  • /middleware 中间件的抽象接口,主要作为 transport 和 service 之间的桥梁适配器。

  • /metadata 跨服务跨协议间的元数据传递及使用

  • /registry 注册中心的抽象接口,可以实现支持各种服务注册与发现中心,如:etcd、consul、nacos。

Transport HTTP/gRPC

kratos v2 框架对传输层进行了抽象,用户也可以实现自己的传输层,框架默认实现了 gRPC 和 HTTP 两种通信协议传输层。Transport 主要的接口:

// 服务的启动和停止,用于管理服务生命周期。
type Server interface {Start(context.Context) errorStop(context.Context) error
}// 用于实现注册到注册中心的终端地址
// 如果不实现这个方法则不会注册到注册中心
type Endpointer interface {Endpoint() (*url.URL, error)
}// 请求头的元数据
type Header interface {Get(key string) stringSet(key string, value string)Keys() []string
}// Transporter is transport context value interface.
type Transporter interface {// 代表实现的通讯协议的类型。Kind() Kind// 提供的服务终端地址。Endpoint() string// 用于标识服务的方法路径Operation() stringRequestHeader() HeaderReplyHeader() Header
}

应用生命周期管理

在 kratos v2 中,可以通过实现 transport.Server 接口,然后通过 kratos.New 启动器进行管理服务生命周期。启动器主要处理:

  • server 生命周期管理

  • registry 注册中心管理

// AppInfo is application context value.
type AppInfo interface {ID() stringName() stringVersion() stringMetadata() map[string]stringEndpoint() []string
}

配置规范的思考

在使用 kratos v2 中,配置源可以指定多个,并且 Config 包会对配置合并成 key/value,然后用户可以通过 Scan 或者 value 获取对应键值的内容,主要功能如下:

  • 内置实现了基于本地文件的数据源。

  • 用户可以通过插件接入自定义数据源如:nacos、consul、apollo 等。

  • 支持配置热更新(watch),通过 Atomic 方式变更已有键的值。

  • 支持自定义数据源 Decode 实现。

  • 支持对 flags、环境变量 占位符的替换。

  • 可以对铺平的 key/value,进行二次赋值替换。

配置规范的思考

在 kratos v2 中,默认通过 proto 定义配置的模板,主要有以下几点好处:

  • 可以定义统一的模板配置

  • 添加对应的配置校验

  • 更好的管理配置

  • 多语言支持

message Bootstrap {Server server = 1;Data data = 2;
}message Server {message HTTP {string network = 1;string addr = 2;google.protobuf.Duration timeout = 3;}message GRPC {string network = 1;string addr = 2;google.protobuf.Duration timeout = 3;}HTTP http = 1;GRPC grpc = 2;
}message Data {message Database {string driver = 1;string source = 2;}message Redis {string network = 1;string addr = 2;google.protobuf.Duration read_timeout = 3;google.protobuf.Duration write_timeout = 4;}Database database = 1;Redis redis = 2;
}
server:http:addr: 0.0.0.0:8000timeout: 1sgrpc:addr: 0.0.0.0:9000timeout: 1s
data:database:driver: mysqlsource: root:root@tcp(127.0.0.1:3306)/testredis:addr: 127.0.0.1:6379read_timeout: 0.2swrite_timeout: 0.2s

业务错误处理

在 kratos v2 中,业务错误主要通过 proto enum 进行定义。在 errors 包中,主要实现了 HTTP 和 gRPC 的接口:

  • StatusCode() int

  • GRPCStatus() *grpc.Status

业务错误,主要参考了 gRPC errdetails.ErrorInfo 的实现:

  • code 错误码,跟 http-status 一致,并且在 grpc 中可以转换为 grpc-status。

  • message 错误信息,用户可读的信息,可作为用户提示内容。

  • reason 错误原因,定义为业务判定的错误码。

  • metadata 错误元信息,可以向错误附加可扩展信息。

实际使用

编写 proto 文件

syntax = "proto3";package helloworld.v1;
import "errors/errors.proto";option go_package = "github.com/go-kratos/kratos-layout/api/helloworld/v1;v1";
option java_multiple_files = true;
option java_package = "helloworld.v1.errors";
option objc_class_prefix = "APIHelloworldErrors";enum ErrorReason {USER_NOT_FOUND = 0;CONTENT_MISSING = 1;
}

在 biz 中依赖 proto enum 定义错误

var (// ErrUserNotFound is user not found.ErrUserNotFound = errors.NotFound(v1.ErrorReason_USER_NOT_FOUND.String(), "user not found")
)

使用错误

func (uc *GreeterUsecase) CreateGreeter(ctx context.Context, g *Greeter) (*Greeter, error) {uc.log.WithContext(ctx).Infof("CreateGreeter: %v", g.Hello)save, err := uc.repo.Save(ctx, g)if err != nil {return nil, ErrUserNotFound.WithMetadata(map[string]string{"error":err.Error()})}return save, nil
}

判定错误

// reasonif err != nil {if errors.Reason(err) == v1.ErrorReason_USER_NOT_FOUND.String() {// TODO: do something}return nil, err}// errors.Asif err != nil {if se := new(errors.Error); errors.As(err,&se) {switch se.Reason {case v1.ErrorReason_USER_NOT_FOUND.String():// TODO: do something}}}// errors.Isif err != nil {if errors.Is(err, ErrUserNotFound) {// TODO: do something}}

日志接口设计

在 kratos v2 日志模块中,主要分为 LoggerHelperFilterValuer 的实现。为了方便扩展,Logger 接口定义非常简单:

type Logger interface {Log(level Level, keyvals ...interface{}) error
}

这个 Logger 接口,非常容易组合和扩展:

// 也可以定义多种日志输出 log.MultiLogger(out, err),例如:info/warn/error,file/agent
logger := log.NewStdLogger(os.Stdout)å
// 根据日志级别进行过虑日志,或者 Key/Value/FilterFunc
logger := log.NewFilter(logger, log.FilterLevel(log.LevelInfo))
// 输出结构化日志
logger.Log(log.LevelInfo, "msg", "log info")

如果需要过滤日志中某些不应该被打印明文的字段,例如 password 等信息,可以通过 `log.NewFilter()` 来实现过滤功能。

logger := log.NewFilter(
log.DefaultLogger,
log.FilterLevel(log.LevelInfo), // 通过 Level 过滤日志
log.FilterKey("password"), // 通过 Key 过滤日志
log.FilterValue("123456"), // 通过 Value 过滤日志
log.FilterFunc(func(level Level, keyvals ...interface{}) bool { // 通过自定义 FilterFuncreturn level == log.LevelError})
logger.Log(log.LevelInfo, "password", "123456") // 输出格式为:password=***

通常在使用日志的过程中,我们可以通过 log.With() 和 Hook 定制 Fields,例如 timestampcallertrace 等。在 kratos 日志模块中,主要通过实现 Valuer 进行定制化。

type Valuer func(ctx context.Context) interface{}
func Value(ctx context.Context, v interface{}) interface{} {if v, ok := v.(Valuer); ok {return v(ctx)}return v
}

所以,我们在 kratos v2 项目中可以这样使用日志模块:

logger := log.NewStdLogger(os.Stdout)
logger = log.NewFilter(logger, log.FilterLevel(log.LevelInfo))
logger = log.With(logger, "app", "helloworld","ts", log.DefaultTimestamp,"caller", log.DefaultCaller,"trace_id", log.TraceID(),"span_id", log.SpanID(),
)
helper := log.NewHelper(logger)
helper.WithContext(ctx).Info("info log")

Metadata 传递和使用

微服务之间主要通过 HTTP/gRPC 进行接口交互,所以在服务架构中应该进行统一的元数据传递和使用。在 HTTP/gRPC 中,其实是通过 HTTP Header 进行传递,在框架中首先通过 metadata 包将元数据封装成 key/value 结构,然后携带到 Transport Header 中。

Metadata 默认 Key 格式为:

  • x-md-blobal-xxx 全局传递,例如 mirrorcolorcriticality

  • x-md-local-xxx 局部传递,例如 caller并且用户可以在 middleware/metadata 中定制自己的 key prefix,配置固定的元数据传递。

使用

Metadata 的主要用法为:

  • 配置 client/server 对应的 middleware/metadata 插件,可以自定义传递 key prefix,或者 metadata 常量,例如 caller

  • 然后通过 metadata 包,NewClientContext 或者 FromServerContext 进行配置或者获取。

// server
grpcSrv := grpc.NewServer(grpc.Address(":9000"),grpc.Middleware(metadata.Server(),),
)
// client
conn, err := grpc.DialInsecure(context.Background(),grpc.WithEndpoint("127.0.0.1:9000"),grpc.WithMiddleware(metadata.Client(),),
)
// 获取
if md, ok := metadata.FromServerContext(ctx); ok {extra = md.Get("x-md-global-extra")
}
// 传递
ctx = metadata.AppendToClientContext(ctx, "x-md-global-extra", "2233")

Middleware 使用

kratos v2 内置了一系列的中间件用于处理日志、指标、跟踪链等通用场景。用户也可以通过实现 Middleware 接口,开发自定义 middleware,进行通用的业务处理,比如用户鉴权等。主要的内置中间件:

  • recovery 用于 recovery panic

  • tracing 用于启用 trace

  • logging 用于请求日志的记录

  • metrics 用于启用 metrics

  • validate 用于处理参数校验

  • metadata 用于启用元信息传递

  • etc...

简化的 DDD 实现

如果你尝试学习 Go,或者你正在为自己建立一个 PoC 或一个玩具项目,这个项目布局是没啥必要的。从一些非常简单的事情开始(一个 main.go 文件绰绰有余)。当有更多的人参与这个项目时,你将需要更多的结构,包括需要一个 Toolkit 来方便生成项目的模板,尽可能大家统一的工程目录布局。

9c4e8e43ff046076a533b1a741c8c6d3.png
.
├── 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 等封装,同时也是 rpc 调用的 acl 防腐层,它实现了 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

未来规划

综上可见,kratos v2 是一款凝结了开源社区力量以及 Go 同学们大量微服务工程实践后诞生的一款微服务框架,现阶段 kratos v2 框架已经功能逐渐完善,后续先期会将精力主要放在 kratos gateway 上,同时会开始 Kratos API interface 和服务治理平台 Kratos ui 的规划。在此也欢迎广大 gopher 加入 kratos 社区参与到 kratos 相关生态的开发中。

相关资料

  • 官网 go-kratos.dev

  • kratos https://github.com/go-kratos/kratos

    kratos gateway https://github.com/go-kratos/gateway

    kratos contrib https://github.com/go-kratos/kratos/tree/main/contrib

    kratos aegis https://github.com/go-kratos/aegis

参考阅读:

  • B+树数据库加锁历史

  • 前端工程化之FaaS SSR方案

  • Kafka 3.0新特性全面曝光,真香!

  • BIGO RTC如何低成本实现高画质

  • 一文读懂 Web3:互联网发展的新时代还是骗局?

本文由高可用架构翻译。技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。

高可用架构

改变互联网的构建方式


http://chatgpt.dhexx.cn/article/DqVd77nn.shtml

相关文章

【kratos入门实战教程】1-kratos项目搭建和开发环境配置

1、系列目录 【kratos入门实战教程】0-商城项目介绍【kratos入门实战教程】1-kratos项目搭建和开发环境配置【kratos入门实战教程】2-实现注册登陆业务 2、概览 经过上一篇的说明,本篇教程正式开始搭建项目。深入解析工程项目的目录结构和介绍一些开发需要使用的工…

go微服务框架Kratos简单使用总结

Kratos是B站开源的一款go的微服务框架,最近PS5上的 战神诸神黄昏比较火,主角就是奎托斯。这个框架的名字就取自他。 在进行框架选型时,对比了目前主流的很多go微服务框架,如Zero,最后对比之下,选择了Krato…

c语言代码后return0表示什么意思,C语言中return 0 表示什么

满意答案 强计划止步不.. 2013.01.03 采纳率:44% 等级:12 已帮助:7948人 return表示函数结束, 也就是说CPU执行到return后, 就会跳转(如果要好理解的话, 是"回到")到调用它的地方, 然后继续执行. 而0, 是一个整型, 一般来说retu…

c语言代码后return0表示什么意思,return 0是什么意思 ?

return 0 代表告诉调用者程序是正常退出的。return是C预定义的语句,它提供了终止函数执行的一种方式。当return语句提供了一个值时,这个值就成为函数的返回值。 这个return语句结束main()函数的执行,把0返还给操作系统。从main()函数返回0表…

接口性能测试方案

一、 性能测试术语解释 1. 响应时间 响应时间即从应用系统发出请求开始,到客户端接收到最后一个字节数据为止所消耗的时间。响应时间按软件的特点再可以细分,如对于一个 C/S 软件(B/S一样)的响应时间可以细分为网络传输时间、应…

软件性能测试方案-性能测试准备

性能测试目的 1、性能调优 开发人员对系统调优后,需要测试人员配合去做性能测试,验证这次优化是否有效果。如果性能指标相比较之前的性能指标更好了,说明系统优化的有效果。反之说明调优不理想 2、新业务、新接口上线 系统从无到上线&…

参考文档一:性能测试---测试方案

性能测试详细测试方案 前言 平台XX项目系统已经成功发布,依据项目的规划,未来势必会出现业务系统中信息大量增长的态势。 随着业务系统在生产状态下日趋稳定、成熟,系统的性能问题也逐步成为了我们关注的焦点:每天大数据量的“…

【性能测试】性能测试方案设计

性能测试方案设计 1. 性能测试流程 系统架构调研 业务模型分析调研 测试需求分析设计测试方案测试环境搭建测试数据准备测试工具开发测试场景设置执行场景测试测试结果分析提交测试报告 2. 性能测试需求分析和范围确定 收集性能需求信息分析应用程序 系统设计和架构信息网…

接口性能测试方案 白皮书 V1.0

一、 性能测试术语解释 1. 响应时间 响应时间即从应用系统发出请求开始,到客户端接收到最后一个字节数据为止所消耗的时间。响应时间按软件的特点再可以细分,如对于一个 C/S 软件的响应时间可以细分为网络传输时间、应用服务器处理时间、数据库服务器处…

性能测试案例

做性能测试之前需要对Linux内核参数优化 Linux内核参数优化 Linux服务器默认支持1024个TCP链接,在性能测试时,无论压力机还是项目服务器,都需要对tcp参数进行一些优化 ulimit -n:查看当前Linux系统最大的连接数 修改Linux系统允许…

性能测试(一)

一)谈谈你对于性能测试的理解: 1)性能测试的概念 测试目的与功能测试的区别性能测试的指标 2)性能测试需要借助工具来进行测试,可以说说自己是用了哪些工具以及如何使用工具来进行性能测试 3)为了避免面试官在性能测试方面进行深究,主动说性能测试难就难…

最全面性能测试方案模板

性能测试方案书 1 简介 1.1目的 编写本文档的目的在于描述测试项目的测试范围,定义测试条件和目标,测试策略和要求,分析可能的风险,提供相应的规避措施或应急对策,并确定测试整体进度的计划和人力资源安排等。 测试目…

性能测试介绍

转载来源 公众号《测试开发技术》 1.性能测试要开专栏了 提起性能测试,可能很多互联网从业人员会感觉比较混淆(不仅仅只是测试人员会弄混淆,很多开发人员、管理人员对性能测试也都是一知半解)。性能测试,它是属于测试…

性能测试流程-性能测试2

性能测试流程 1. 性能测试需求分析 2. 性能测试计划及方案 3. 性能测试用例 4. 搭建测试环境 5. 测试数据准备 6. 测试脚本编写 7. 执行测试脚本 8. 性能测试监控 9. 性能分析和调优 10. 性能测试回归 11. 性能测试报告总结 1、性能测试需求分析 熟悉被测系统 熟…

【测试方案】性能方案设计

什么是性能测试? 压力测试:强调极端暴力 稳定性测试:在一定压力下,长时间运行的情况 基准测试:在特定条件下的性能测试 负载测试:不同负载下的表现 容量测试:最优容量 性能测试方案设计流程 需…

性能测试—性能测试方案设计思路总结

一、需求分析 1.测试目的 为什么测?目的在于测试系统相关性能能否满足业务需求。通常分以下两种情况: 1)新项目上线 2)老项目优化 如果是老项目优化,可考虑是否存有历史测试方案,如果有可以参考&#…

性能测试计划及方案

说明:性能测试实施第一份文档,也是最重要的一份文档。 主要内容: 1、项目背景 项目是用来做什么的 2、测试目标 确定此次性能测试的目标,决定测试策略(下文) 3、人员安排 明确性能测试的时间&#xff0c…

性能测试详细测试方案

性能测试详细测试方案 前言 平台XX项目系统已经成功发布,依据项目的规划,未来势必会出现业务系统中信息大量增长的态势。 随着业务系统在生产状态下日趋稳定、成熟,系统的性能问题也逐步成为了我们关注的焦点:每天大数据量的“…

性能测试设计方案

前言 最近比较多的时间是在做压力测试,也就是常说的压测,而且压力测试也属于性能测试中的其中一个,所以今天总结下性能测试的测试方案。 性能测试分类 常见的性能测试分类一般分为:负载测试、压力测试、稳定性测试 ①负载测试&…

​性能测试基础——性能测试方案(示例)

前面所说的测试分析等准备工作实际上最终目的是制定测试方案,测试方案一般包括: 项目的简要说明、项目系统结构、项目的业务结构、以及项目的性能需求、测试环境数据以及测试策略方法、测试案例、测试人员进度安排以及测试风险预估等等。 下面是一个一般…