简介
上线时间:
2018年08月
概念:
go-zero 是一个集成了各种工程实践的web和rpc框架。包含极简的api定义和生成工具goctl,可以根据定义的api文件一键生成go、ios、android、kotlin、dart、typeScript、javaScript代码,并可直接运行。
特点:
- 强大的工具支持,尽可能少的代码编写
2. 极简的接口
3. 完全兼容net/http
4. 支持中间件,方便扩展
5. 高性能
6. 面向故障编程(解释:a.故障感知 b.优雅的故障处理 c.日志和实时监测,以供问题的排查和修复),弹性设计
7. 内建服务发现、负载均衡
8. 内建限流、熔断、降载,且自动触发自动回复
9. api参数自动校验
10. 超时级联控制
11. 自动缓存控制
12. 链路跟踪、统计报警等
13. 高并发支撑,稳定保障了疫情期间每天的流量洪峰
开发三原则:
- Clarity(清晰):清晰的代码
- Simplicity(简单):程序简单
- Productivity(生产力):开发效率最大化
框架设计
api文件
/**
* api语法示例及语法说明
*/
// api语法声明版本(正则:v[1-9][0-9]*)
syntax = "v1"
// 导入其他api文件(正则:/?[a-zA-Z0-9_#-])+\.api)
// 原理:仅仅是文件路径的引入,最终解析后会把所有的声明汇聚到一个spec.Spec中
// 注:被import的api必须要和main api的syntax版本一致)
import "foo.api"
// import group
import (
"bar.api"
"foo/bar.api"
)
info(
author: "songmeizi"
date: "2020-01-08"
desc: "api语法示例及语法说明"
)
// type literal
type Person{
Name string `json:"name,optional"` // optional:当前字段为可选参数
Age int `json:"age,range=[0:120]"` // range:当前字段数值范围在0-120内
Gender string `json:"gender,options=male|female"`// options=male|female:当前字段的枚举值,多个以|分隔
}
// type group
type(
Bar{
Bar int `json:"bar"`
}
)
// service block
@server(
jwt: Auth // 表示当前service下所有路由需要jwt鉴权,且会自动生成包含jwt逻辑的代码
group: foo // 当前service和路由文件分组
middleware: AuthMiddleware // 表示当前service需要开启中间件
prefix: api // 表示路由前缀
)
service foo-api{
@doc "foo"
@handler foo
post /foo (Foo) returns (Bar)
@doc "foo"
@handler foo
post /foo/:id (Foo) returns (Bar)
}
api目录
.
├── etc
│ └── greet-api.yaml // 配置文件
├── go.mod // mod文件
├── greet.api // api描述文件
├── greet.go // main函数入口
└── internal
├── config
│ └── config.go // 配置声明type
├── handler // 路由及handler转发
│ ├── greethandler.go
│ └── routes.go
├── logic // 业务逻辑
│ └── greetlogic.go
├── middleware // 中间件文件
│ └── greetmiddleware.go
├── svc // logic所依赖的资源池
│ └── servicecontext.go
└── types // request、response的struct,根据api自动生成,不建议编辑
└── types.go
proto文件
syntax = "proto3";
package stream;
// 自动生成pb.go & _grpc.pb.go 文件路径
option go_package = "./greet";
message StreamReq {
string name = 1;
}
message StreamResp {
string greet = 1;
}
service StreamGreeter {
rpc greet(StreamReq) returns (StreamResp);
}
rpc目录
.
├── etc
│ └── greet.yaml
├── go.mod
├── go.sum
├── greet // [1]
│ ├── greet.pb.go
│ └── greet_grpc.pb.go
├── greet.go
├── greet.proto
├── internal
│ ├── config
│ │ └── config.go
│ ├── logic
│ │ └── greetlogic.go
│ ├── server
│ │ └── streamgreeterserver.go
│ └── svc
│ └── servicecontext.go
└── streamgreeter
└── streamgreeter.go
链路追踪
type SpanContext struct {
traceId string // TraceID 表示tracer的全局唯一ID
spanId string // SpanId 标示单个trace中某一个span的唯一ID,在trace中唯一
}
type Span struct {
ctx spanContext // 传递的上下文
serviceName string // 服务名
operationName string // 操作
startTime time.Time // 开始时间戳
flag string // 标记开启trace是 server 还是 client
children int // 本 span fork出来的 childsnums
}
api
- 通过request.header生成carrier。carrier = trace.Extract(trace.HttpFormat, request.Header)
- 通过request.context和carrier开启一个新的span。新的span ctx是由carrier.traceId和carrier.spanId组成,当carrier不存在,则随机生成返回traceId和spanId。span.ctx = carrier.traceId + carrier.spanId
- 从request中产生新的ctx,并将request.context和新span封装到新ctx中,返回新ctx和新span。ctx = context.WithValue(ctx, tracespec.TracingKey, span)
- 设置到request。request = request.WithContext(ctx)
rpc
- 获取上游带来的span上下文信息。ctx, span := trace.StartClientSpan(ctx, cc.Target(), method)
- 从获取的span中创建新的span,span继承父span的traceId。span.Fork(ctx, serviceName, operationName)
- 将生成的span.data加入ctx,传递到下一个中间件,流至下游。 ctx = metadata.AppendToOutgoingContext(ctx, pairs…)
总结
go-zero通过拦截请求获取链路traceID,然后在中间件函数入口会分配一个根Span,然后在后续操作中会分裂出子Span,每个span都有自己的具体的标识,Finsh之后就会汇集在链路追踪系统中。开发者可以通过ELK(elasticsearch(存储+搜索)、logstash(收集)、kibana(展示),)工具追踪traceID,看到整个调用链。
同时go-zero并没有提供整套trace链路方案,开发者可以封装go-zero已有的span结构,做自己的上报系统,接入jeager、zipkin等链路追踪工具。
启动原理
api
一、解析配置文件
二、 注册依赖
三、初始化工具(logger日志记录、prometheus性能监控告警、trace链路、metrics性能监控)、配置和服务
四、 注册路由
五、启动服务(1.加上中间件 2.路由绑定到router上 3.启动http/https服务)
六、等待服务结束
type Server struct {
ngin *engine(三)
router httpx.Router(三、五)
}
type engine struct {
conf RestConf(三)
routes []featuredRoutes(四)
unauthorizedCallback handler.UnauthorizedCallback
unsignedCallback handler.UnsignedCallback
chain chain.Chain
middlewares []Middleware
shedder load.Shedder
priorityShedder load.Shedder
tlsConfig *tls.Config
}
rpc
一、解析配置文件
二、 注册依赖
三、 加载rpc接口
三、初始化工具(etcd、logger日志记录、prometheus性能监控告警、trace链路、metrics性能监控)、注册服务
五、启动服务(1.加上中间件 2.健康监听 3.启动服务)
六、等待服务结束
type RpcServer struct {
server internal.Server (四)
register internal.RegisterFn (四)
}
type keepAliveServer struct {
registerEtcd func() error (四)
internal.Server (四)
}