你们好,我是煎鱼,在实际应用中,你作了那么多 Server 端,写了 N 个 RPC 方法。想看看方法的指标,却无处下手?html
本文将经过 gRPC + Opentracing + Zipkin 搭建一个分布式链路追踪系统来实现查看整个系统的链路、性能等指标。git
OpenTracing 经过提供平台无关、厂商无关的API,使得开发人员可以方便的添加(或更换)追踪系统的实现github
不过 OpenTracing 并非标准。由于 CNCF 不是官方标准机构,可是它的目标是致力为分布式追踪建立更标准的 API 和工具golang
一个 trace 表明了一个事务或者流程在(分布式)系统中的执行过程docker
一个 span 表明在分布式系统中完成的单个工做单元。也包含其余 span 的 “引用”,这容许将多个 spans 组合成一个完整的 Trace后端
每一个 span 根据 OpenTracing 规范封装如下内容:api
Span tags(跨度标签)能够理解为用户自定义的 Span 注释。便于查询、过滤和理解跟踪数据bash
Span logs(跨度日志)能够记录 Span 内特定时间或事件的日志信息。主要用于捕获特定 Span 的日志信息以及应用程序自己的其余调试或信息输出架构
SpanContext 表明跨越进程边界,传递到子级 Span 的状态。常在追踪示意图中建立上下文时使用app
Baggage Items 能够理解为 trace 全局运行中额外传输的数据集合
图中能够看到如下内容:
结合以上信息,在实际场景中咱们能够经过整个系统的调用链的上下文、性能等指标信息,一会儿就可以发现系统的痛点在哪儿
Zipkin 是分布式追踪系统。它的做用是收集解决微服务架构中的延迟问题所需的时序数据。它管理这些数据的收集和查找
Zipkin 的设计基于 Google Dapper 论文。
docker run -d -p 9411:9411 openzipkin/zipkin
复制代码
其余方法安装参见:github.com/openzipkin/…
访问 http://127.0.0.1:9411/zipkin/ 检查 Zipkin 是否运行正常
在前面的小节中,咱们作了如下准备工做:
接下来实现 gRPC 经过 Opentracing 标准 API 对接 Zipkin,再经过 Zipkin 去查看数据
新建 simple_zipkin_client、simple_zipkin_server 目录,目录结构以下:
go-grpc-example
├── LICENSE
├── README.md
├── client
│ ├── ...
│ ├── simple_zipkin_client
├── conf
├── pkg
├── proto
├── server
│ ├── ...
│ ├── simple_zipkin_server
└── vendor
复制代码
$ go get -u github.com/openzipkin/zipkin-go-opentracing
$ go get -u github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc
复制代码
package main
import (
"context"
"log"
"net"
"github.com/grpc-ecosystem/go-grpc-middleware"
"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
zipkin "github.com/openzipkin/zipkin-go-opentracing"
"google.golang.org/grpc"
"github.com/EDDYCJY/go-grpc-example/pkg/gtls"
pb "github.com/EDDYCJY/go-grpc-example/proto"
)
type SearchService struct{}
func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) {
return &pb.SearchResponse{Response: r.GetRequest() + " Server"}, nil
}
const (
PORT = "9005"
SERVICE_NAME = "simple_zipkin_server"
ZIPKIN_HTTP_ENDPOINT = "http://127.0.0.1:9411/api/v1/spans"
ZIPKIN_RECORDER_HOST_PORT = "127.0.0.1:9000"
)
func main() {
collector, err := zipkin.NewHTTPCollector(ZIPKIN_HTTP_ENDPOINT)
if err != nil {
log.Fatalf("zipkin.NewHTTPCollector err: %v", err)
}
recorder := zipkin.NewRecorder(collector, true, ZIPKIN_RECORDER_HOST_PORT, SERVICE_NAME)
tracer, err := zipkin.NewTracer(
recorder, zipkin.ClientServerSameSpan(false),
)
if err != nil {
log.Fatalf("zipkin.NewTracer err: %v", err)
}
tlsServer := gtls.Server{
CaFile: "../../conf/ca.pem",
CertFile: "../../conf/server/server.pem",
KeyFile: "../../conf/server/server.key",
}
c, err := tlsServer.GetCredentialsByCA()
if err != nil {
log.Fatalf("GetTLSCredentialsByCA err: %v", err)
}
opts := []grpc.ServerOption{
grpc.Creds(c),
grpc_middleware.WithUnaryServerChain(
otgrpc.OpenTracingServerInterceptor(tracer, otgrpc.LogPayloads()),
),
}
...
}
复制代码
总的来说,就是初始化 Zipkin,其又包含收集器、记录器、跟踪器。再利用拦截器在 Server 端实现 SpanContext、Payload 的双向读取和管理
func main() {
// the same as zipkin server
// ...
conn, err := grpc.Dial(":"+PORT, grpc.WithTransportCredentials(c),
grpc.WithUnaryInterceptor(
otgrpc.OpenTracingClientInterceptor(tracer, otgrpc.LogPayloads()),
))
...
}
复制代码
(1)OpenTracing SpanContext 注入 gRPC Metadata
(2)查看 context.Context 中的上下文关系,若存在父级 Span 则建立一个 ChildOf 引用,获得一个子 Span
其余方面,与 Server 端是一致的,先初始化 Zipkin,再增长 Client 端特需的拦截器。就能够完成基础工做啦
启动 Server.go,执行 Client.go。查看 http://127.0.0.1:9411/zipkin/ 的示意图:
来,本身实践一下。
在多服务下的架构下,串行、并行、服务套服务是一个很是常见的状况,用常规的方案每每很难发现问题在哪里(成本太大)。而这种状况就是分布式追踪系统大展拳脚的机会了
但愿你经过本章节的介绍和学习,可以了解其概念和搭建且应用一个追踪系统。
若是有任何疑问或错误,欢迎在 issues 进行提问或给予修正意见,若是喜欢或对你有所帮助,欢迎 Star,对做者是一种鼓励和推动。
跟煎鱼学 Go:github.com/eddycjy/blo…