【go-zero】

简介

上线时间:

2018年08月

概念:

go-zero 是一个集成了各种工程实践的web和rpc框架。包含极简的api定义和生成工具goctl,可以根据定义的api文件一键生成go、ios、android、kotlin、dart、typeScript、javaScript代码,并可直接运行。

特点:
  1. 强大的工具支持,尽可能少的代码编写
    2. 极简的接口
    3. 完全兼容net/http
    4. 支持中间件,方便扩展
    5. 高性能
    6. 面向故障编程(解释:a.故障感知 b.优雅的故障处理 c.日志和实时监测,以供问题的排查和修复),弹性设计
    7. 内建服务发现、负载均衡
    8. 内建限流、熔断、降载,且自动触发自动回复
    9. api参数自动校验
    10. 超时级联控制
    11. 自动缓存控制
    12. 链路跟踪、统计报警等
    13. 高并发支撑,稳定保障了疫情期间每天的流量洪峰
开发三原则:
  1. Clarity(清晰):清晰的代码
  2. Simplicity(简单):程序简单
  3. 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
  1. 通过request.header生成carrier。carrier = trace.Extract(trace.HttpFormat, request.Header)
  2. 通过request.context和carrier开启一个新的span。新的span ctx是由carrier.traceId和carrier.spanId组成,当carrier不存在,则随机生成返回traceId和spanId。span.ctx = carrier.traceId + carrier.spanId
  3. 从request中产生新的ctx,并将request.context和新span封装到新ctx中,返回新ctx和新span。ctx = context.WithValue(ctx, tracespec.TracingKey, span)
  4. 设置到request。request = request.WithContext(ctx)
rpc
  1. 获取上游带来的span上下文信息。ctx, span := trace.StartClientSpan(ctx, cc.Target(), method)
  2. 从获取的span中创建新的span,span继承父span的traceId。span.Fork(ctx, serviceName, operationName)
  3. 将生成的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 (四)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值