github完整代码地址 我的博客html
jaeger提供一个all in one 的docker镜像,能够快速搭建实验环境git
docker run -d --name jaeger
-e COLLECTOR_ZIPKIN_HTTP_PORT=9411
-p 5775:5775/udp
-p 6831:6831/udp
-p 6832:6832/udp
-p 5778:5778
-p 16686:16686
-p 14268:14268
-p 9411:9411
jaegertracing/all-in-one:1.6
复制代码
OpenTracing经过提供平台无关、厂商无关的API,使得开发人员可以方便的添加(或更换)追踪系统的实现。 OpenTracing提供了用于运营支撑系统的和针对特定平台的辅助程序库。 jaeger兼容OpenTracing API,因此咱们使用OpenTracing的程序库能够方便的替换追踪工具。 OpenTracing中文文档github
封住一下jaeger的初始化操做方便使用,详细用法能够查看 jaeger-client-gogolang
// lib/tracer
// NewTracer 建立一个jaeger Tracer
func NewTracer(servicename string, addr string) (opentracing.Tracer, io.Closer, error) {
cfg := jaegercfg.Configuration{
ServiceName: servicename,
Sampler: &jaegercfg.SamplerConfig{
Type: jaeger.SamplerTypeConst,
Param: 1,
},
Reporter: &jaegercfg.ReporterConfig{
LogSpans: true,
BufferFlushInterval: 1 * time.Second,
},
}
sender, err := jaeger.NewUDPTransport(addr, 0)
if err != nil {
return nil, nil, err
}
reporter := jaeger.NewRemoteReporter(sender)
// Initialize tracer with a logger and a metrics factory
tracer, closer, err := cfg.NewTracer(
jaegercfg.Reporter(reporter),
)
return tracer, closer, err
}
func main() {
t, io, err := tracer.NewTracer("tracer", "")
if err != nil {
log.Fatal(err)
}
defer io.Close()
opentracing.SetGlobalTracer(t)
}
复制代码
opentracing.SetGlobalTracer(t) 方法执行会将jaeger tracer注册到全局,接下来只须要使用opentracing 的标准API即可以了。 若是不想使用jaeger了,想替换成其余分布式追踪工具,只须要工具支持opentracing标准,并将main函数的SetGlobalTracer操做替换便可,其余文件都不须要更改。docker
在micro自带的插件中已经有opentracing的插件了,包含server,client等,不过这个插件只能go-micro构建的微服务(api,srv)中使用。由于micro网关有一个独立的插件系统,可是并无提供opentracing相关的插件。api
micro/go-plugins/wrapper/trace/opentracing/opentracing.gobash
咱们能够在构建服务的时候直接使用,只须要在服务初始化时增长一行函数就能够了。app
service := micro.NewService(
micro.Name(name),
micro.Version("latest"),
micro.WrapHandler(ocplugin.NewHandlerWrapper(opentracing.GlobalTracer())),
)
复制代码
srv/user/main.go 目录下的user 服务是一个完整的使用实例。分布式
// micro opentracing插件中wHandlerWrappe
// NewHandlerWrapper accepts an opentracing Tracer and returns a Handler Wrapper
func NewHandlerWrapper(ot opentracing.Tracer) server.HandlerWrapper {
return func(h server.HandlerFunc) server.HandlerFunc {
return func(ctx context.Context, req server.Request, rsp interface{}) error {
name := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
ctx, span, err := traceIntoContext(ctx, ot, name)
if err != nil {
return err
}
defer span.Finish()
return h(ctx, req, rsp)
}
}
}
复制代码
lib/wrapper/tracer/opentracing/stdhttp/stdhttp.go函数
和实现JWT鉴权插件同样,实现一个HTTP中间件经过mciro的插件机制全局注册就能够实现拦截每次请求并处理。
// TracerWrapper tracer wrapper
func TracerWrapper(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
spanCtx, _ := opentracing.GlobalTracer().Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
sp := opentracing.GlobalTracer().StartSpan(r.URL.Path, opentracing.ChildOf(spanCtx))
defer sp.Finish()
if err := opentracing.GlobalTracer().Inject(
sp.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(r.Header)); err != nil {
log.Println(err)
}
sct := &status_code.StatusCodeTracker{ResponseWriter: w, Status: http.StatusOK}
h.ServeHTTP(sct.WrappedResponseWriter(), r)
ext.HTTPMethod.Set(sp, r.Method)
ext.HTTPUrl.Set(sp, r.URL.EscapedPath())
ext.HTTPStatusCode.Set(sp, uint16(sct.Status))
if sct.Status >= http.StatusInternalServerError {
ext.Error.Set(sp, true)
}
})
}
复制代码
Tracer相关的概念能够查看这个文档
lib/wrapper/tracer/opentracing/gin2micro/gin2micro.go
// TracerWrapper tracer 中间件
func TracerWrapper(c *gin.Context) {
md := make(map[string]string)
spanCtx, _ := opentracing.GlobalTracer().Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(c.Request.Header))
sp := opentracing.GlobalTracer().StartSpan(c.Request.URL.Path, opentracing.ChildOf(spanCtx))
defer sp.Finish()
if err := opentracing.GlobalTracer().Inject(sp.Context(),
opentracing.TextMap,
opentracing.TextMapCarrier(md)); err != nil {
log.Log(err)
}
ctx := context.TODO()
ctx = opentracing.ContextWithSpan(ctx, sp)
ctx = metadata.NewContext(ctx, md)
c.Set(contextTracerKey, ctx)
c.Next()
statusCode := c.Writer.Status()
ext.HTTPStatusCode.Set(sp, uint16(statusCode))
ext.HTTPMethod.Set(sp, c.Request.Method)
ext.HTTPUrl.Set(sp, c.Request.URL.EscapedPath())
if statusCode >= http.StatusInternalServerError {
ext.Error.Set(sp, true)
}
}
// ContextWithSpan 返回context
func ContextWithSpan(c *gin.Context) (ctx context.Context, ok bool) {
v, exist := c.Get(contextTracerKey)
if exist == false {
ok = false
return
}
ctx, ok = v.(context.Context)
return
}
复制代码
基本操做流程和给micro编写的插件相同,可是有两点不一样。其一,由于我使用gin开发API服务,因此基于gin的API。其二,由于micro内部提供经过golang context传递spanContexts的机制,因此将这边会将child spanContexts注入到gin 的context,在API服务经过micro提供RPC接口(生成的XX.micro.go文件中调用函数第一个参数都是context)调用其余服务时传入提取的context,以下:
...
ctx, ok := gin2micro.ContextWithSpan(c)
if ok == false {
log.Log("get context err")
}
res, err := s.helloC.Call(ctx, &helloS.Request{Name: "xuxu"})
...
复制代码
完整的实现细节能够查看,github仓库中 lib/wrapper/tracer/opentracing, 这里。