kratos 源码解析 TODO
如果是一个简单的需求,那么我们用 Go http、gin、beego 足以
再复杂点呢,接口多起来了,我们寻求团队分工合作,功能解藕,所以按功能、层级将服务进行拆分,服务间使用 http、grpc 进行调用
当服务多起来了,我们需要一个规范统一各个服务的代码,这里的代码是项目层级、中间件调用、日志、配置中心、链路追踪等等,那么这个规范的集合可以沉淀为微服务框架
kratos
应读“奎托斯”,之前读错还带偏面试官,真是尴尬😅
一、使用手册
https://go-kratos.dev/docs/getting-started/start
二、源码功能解析
1. 代码生成
1.1 new
1.2 proto
proto add
proto client
proto server
1.3 wire 依赖注入
2. Metadata 传递
https://go-kratos.dev/docs/component/metadata
使用非常简单,摘一下官网的例子
// server
grpcSrv := grpc.NewServer(
grpc.Address(":9000"),
grpc.Middleware(
metadata.Server(),
),
)
httpSrv := http.NewServer(
http.Address(":8000"),
http.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")
说完使用,来说原理,本质上就是在中间层 做 metadata 的正反序列化,借助 header 这个载体实现(注意,metadata 的middleware 只是把元信息注入到 header,具体正反序列化由 transport 去做)
kratos/metadata/metadata.go 对 metadata 结构体进行抽象定义
type Metadata map[string][]string
kratos/middleware/tracing/metadata.go
对于server端来说,提取 transport 抽象,获取调用方特定 prefix(有默认,也可以追加自定义) 的 header 加载到 ctx 中
func Server(opts ...Option) middleware.Middleware {
options := &options{
prefix: []string{"x-md-"}, // x-md-global-, x-md-local
}
for _, o := range opts {
o(options)
}
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
tr, ok := transport.FromServerContext(ctx)
if !ok {
return handler(ctx, req)
}
md := options.md.Clone()
header := tr.RequestHeader()
for _, k := range header.Keys() {
if options.hasPrefix(k) {
for _, v := range header.Values(k) {
md.Add(k, v)
}
}
}
ctx = metadata.NewServerContext(ctx, md)
return handler(ctx, req)
}
}
}
对于 client 来说,将 ctx 中所有的元信息都注入到 header,也就是说我们可以携带一些元信息传递给被调用方(注意在创建 grpc client 时 要引用如下 middleware)
// Client is middleware client-side metadata.
func Client(opts ...Option) middleware.Middleware {
options := &options{
prefix: []string{"x-md-global-"},
}
for _, o := range opts {
o(options)
}
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
tr, ok := transport.FromClientContext(ctx)
if !ok {
return handler(ctx, req)
}
header := tr.RequestHeader()
// x-md-local-
for k, vList := range options.md {
for _, v := range vList {
header.Add(k, v)
}
}
if md, ok := metadata.FromClientContext(ctx); ok {
for k, vList := range md {
for _, v := range vList {
header.Add(k, v)
}
}
}
// x-md-global-
if md, ok := metadata.FromServerContext(ctx); ok {
for k, vList := range md {
if options.hasPrefix(k) {
for _, v := range vList {
header.Add(k, v)
}
}
}
}
return handler(ctx, req)
}
}
}
3. Transport
3.1 传输层
https://go-kratos.dev/docs/component/transport/overview
kratos 可以一份代码,http、grpc 都用,所依托的就是抽象应用层协议,见transport.go
有两个重要抽象
type Server interface {
Start(context.Context) error
Stop(context.Context) error
}
// Transporter is transport context value interface.
type Transporter interface {
// Kind transporter
// grpc
// http
Kind() Kind
// Endpoint return server or client endpoint
// Server Transport: grpc://127.0.0.1:9000
// Client Transport: discovery:///provider-demo
Endpoint() string
// Operation Service full method selector generated by protobuf
// example: /helloworld.Greeter/SayHello
Operation() string
// RequestHeader return transport request header
// http: http.Header
// grpc: metadata.MD
RequestHeader() Header
// ReplyHeader return transport reply/response header
// only valid for server transport
// http: http.Header
// grpc: metadata.MD
ReplyHeader() Header
}
在看 http 和 grpc 各自的实现时,还遇到一些源码中常出现的范式
// 这里括号里的 Server 指的是 http 对应实现的 struct Server,清晰地表明了结构体实现了哪些 interface
var (
_ transport.Server = (*Server)(nil)
_ transport.Endpointer = (*Server)(nil)
_ http.Handler = (*Server)(nil)
)
因为 kratos 是以 grpc 代码为准,所以自己需要实现 proto 中定义的 Service,那么如果希望 http 也可以直接用这部分 Service 逻辑,那么就需要将 http 路由到对应 Service 方法下,并且做好 入反参序列化、middleware、context 等抽象
3.2 路由与负载均衡
https://go-kratos.dev/docs/component/selector
4. Config
https://go-kratos.dev/docs/component/config
5. Logger
https://go-kratos.dev/docs/component/log
6. Metrics
https://go-kratos.dev/docs/component/metrics
7. Tracing
https://go-kratos.dev/docs/component/middleware/tracing
8. Encoding
https://go-kratos.dev/docs/component/encoding