go基于grpc构建微服务框架-集成opentracing

1.概述

存在这样一种场景,当我们进行微服务拆分后,一个请求将会经过多个服务处理之后再返回,这时,如果在请求的链路上某个服务出现故障时,排查故障将会比较困难.
我们可能需要将请求经过的服务,挨个查看日志进行分析,当服务有几十上百个实例时,这无疑是可怕的.因此为了解决这种问题,调用链追踪应运而生.

2.opentracing

1.1 opentracing作用

调用链追踪最先由googel在Dapper这篇论文中提出,OpenTracing主要定义了相关的协议以及接口,这样各个语言只要按照Opentracing的接口以及协议实现数据上报,那么调用信息就能统一被收集.

如上图所示,接口可能首先经过web框架,然后调用auth服务,通过调用链,将请求经过的服务进行编号,统一收集起来,形成逻辑上的链路,这样,我们就可以看到请求经过了哪些服务,从而形成服务依赖的拓扑.

如上,总链路由每段链路组成,每段链路均代表经过的服务,耗时可用于分析系统瓶颈,当某个请求返回较慢时,可以通过排查某一段链路的耗时情况,从而分析是哪个服务出现延时较高,今个到具体的服务中分析具体的问题.

1.2 opentraing关键术语

  • Traces(调用链)

一次调用的链路,由TraceID唯一标志,如一次请求则通常为一个trace,trace由所有途径的span组成.

  • Spans(调用跨度)

没进过一个服务则将span,同样每个span由spanID唯一标志.

  • Span Tags(跨度标签)

span的标签,如一段span是调用redis的,而可以设置redis的标签,这样通过搜索redis关键字,我们就可以查询出所有相关的span以及trace.

  • Baggage Item(附带数据)

附加的数据,由key:value组成,通过附加数据,可以给调用链更多的描述信息,不过考虑到传输问题,附加数据应该尽可能少.

1.3 jaeger & zipkin

目前开源的实现有zipkin以及jaeger

  • zipkin

zipkin主要由java编写,通过各个语言的上报库实现将数据上报到collector,collector再将数据存储,并通过API提供给前段UI展示.

  • jaeger

jaeger由go实现,由uber开发,目前是cloud native项目,流程与zipkin类似,增加jager-agent这样个组件,这个组件官方建议是每个机器都部署一个,通过这个组件再将数据上报到collector存储展示,另外,里面做了对zipkin的适配,其实一开始他们用的也是zipkin,为毛后面要自己造轮子?见他们的解释. 链接

总的来说两者都能基本满足opentracing的功能,具体的选择可以结合自身技术栈和癖好.

2. grpc集成opentracing

grpc集成opentracing并不难,因为grpc服务端以及调用端分别声明了UnaryClientInterceptor以及UnaryServerInterceptor两个回调函数,因此只需要重写这两个回调函数,并在重写的回调函数中调用opentracing接口进行上报即可.
初始化时传入重写后的回调函数,同时二选一初始化jager或者zipkin,然后你就可以开启分布式调用链追踪之旅了.

完整的代码见grpc-wrapper

2.1 client端

// OpenTracingClientInterceptor  rewrite client's interceptor with open tracing
func OpenTracingClientInterceptor(tracer opentracing.Tracer) grpc.UnaryClientInterceptor {
    return func(
        ctx context.Context,method string,req,resp interface{},cc *grpc.ClientConn,invoker grpc.UnaryInvoker,opts ...grpc.CallOption,) error {

        //从context中获取spanContext,如果上层没有开启追踪,则这里新建一个
        //追踪,如果上层已经有了,测创建子span.
        var parentCtx opentracing.SpanContext
        if parent := opentracing.SpanFromContext(ctx); parent != nil {
            parentCtx = parent.Context()
        }
        cliSpan := tracer.StartSpan(
            method,opentracing.ChildOf(parentCtx),wrapper.TracingComponentTag,ext.SpanKindRPCClient,)
        defer cliSpan.Finish()

        //将之前放入context中的metadata数据取出,如果没有则新建一个metadata
        md,ok := metadata.FromOutgoingContext(ctx)
        if !ok {
            md = metadata.New(nil)
        } else {
            md = md.Copy()
        }
        mdWriter := MDReaderWriter{md}

        //将追踪数据注入到metadata中
        err := tracer.Inject(cliSpan.Context(),opentracing.TextMap,mdWriter)
        if err != nil {
            grpclog.Errorf("inject to metadata err %v",err)
        }
        //将metadata数据装入context中
        ctx = metadata.NewOutgoingContext(ctx,md)
        //使用带有追踪数据的context进行grpc调用.
        err = invoker(ctx,method,resp,cc,opts...)
        if err != nil {
            cliSpan.LogFields(log.String("err",err.Error()))
        }
        return err
    }
}

2.2 server端

//OpentracingServerInterceptor rewrite server's interceptor with open tracing
func OpentracingServerInterceptor(tracer opentracing.Tracer) grpc.UnaryServerInterceptor {
    return func(
        ctx context.Context,req interface{},info *grpc.UnaryServerInfo,handler grpc.UnaryHandler,) (resp interface{},err error) {
                //从context中取出metadata
        md,ok := metadata.FromIncomingContext(ctx)
        if !ok {
            md = metadata.New(nil)
        }
               //从metadata中取出最终数据,并创建出span对象
        spanContext,err := tracer.Extract(opentracing.TextMap,MDReaderWriter{md})
        if err != nil && err != opentracing.ErrSpanContextNotFound {
            grpclog.Errorf("extract from metadata err %v",err)
        }
                //初始化server 端的span
        serverSpan := tracer.StartSpan(
            info.FullMethod,ext.RPCServerOption(spanContext),ext.SpanKindRPCServer,)
        defer serverSpan.Finish()
        ctx = opentracing.ContextWithSpan(ctx,serverSpan)
             //将带有追踪的context传入应用代码中进行调用
        return handler(ctx,req)
    }
}

由于opentracing定义了相关的接口,而jaeger以及zipkin进行了相应的实现,因此这里可以使用jaeger的也可以使用zipkin进行上报.

3.效果

jaeger服务主页信息

每条调用链信息

4.参考

zipkin
jaeger
OpenTracing
grpc-wrapper

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


gRPC 前言 为什么使用gRPC 传输协议 传输效率 性能消耗 gRPC入门 gRPC流 证书认证 使用根证书 gRPC实现token认证 和Web服务共存 验证器 REST接口 grpcurl工具
参考文章: 1. https://www.cnblogs.com/kaixinyufeng/p/9651513.html 2. http://jia-shun.cn/2018/08
今天给大家翻译一篇由ASP.NET首席开发工程师 "James Newton King" 前几天发表的一篇博客,文中带来了一个实验性的产品gRPC Web。大家可以点击文末的讨论帖
上一篇文章我带着大家体验了一把《 "ASP.NET Core 3.0 上的gRPC服务模板初体验(多图)" 》,如果有兴趣的可以点击链接进行查看,相信跟着做的你,也是可以跑起来的。
早就听说ASP.NET Core 3.0中引入了gRPC的服务模板,正好趁着家里电脑刚做了新系统,然后装了VS2019的功夫来体验一把。同时记录体验的过程。如果你也想按照本文的步骤体验的话,那你得先安
这篇笔记主要是记录学习历程而不是怎么用~,以及protobuffers 和 gprc 各种文档的地址,等过上大半年后通过这篇笔记帮助自己快速重新掌握这个技术点 一、Protocolbuffers 关于
最近GRPC很火,感觉整RPC不用GRPC都快跟不上时髦了。 gRPC设计 gRPC是一种与语言无关的高性能远程过程调用 (RPC) 框架。刚好需要使用一个的RPC应用系统,自然而然就盯上了它,但是它
   gRPC是google开源提供的一个RPC软件框架,它的特点是极大简化了传统RPC的开发流程和代码量,使用户可以免除许多陷阱并聚焦于实际应用逻辑中。作为一种google的最新RPC解决方案,gRPC具备了以下这些强项: 1、gRPC在HTTP/2协议上用protobuf取代了json实现了最佳效率 2、用IDL(Interface Definition Language),一种简单的描述语言
  接着上期讨论的gRPC unary服务我们跟着介绍gRPC streaming,包括: Server-Streaming, Client-Streaming及Bidirectional-Streaming。我们首先在.proto文件里用IDL描述Server-Streaming服务: /* * responding stream of increment results */ servi
我已经设法通过GRPC使用流媒体模式的服务帐户为我的 Android应用程序运行Google Cloud Speech.但是,根据我所读到的内容,出于安全原因,我不应该在其中部署具有这些凭据的Android应用程序(当前存储为资源中的JSON文件).正确的是创建一个API密钥,如下所述: https://cloud.google.com/speech/docs/common/auth 这允许我限制
  安装protobuf go get -u github.com/golang/protobuf/proto go get -u github.com/golang/protobuf/protoc-gen-go 此时会生成protoc-gen-go,protoc一般是获取已经编译好的可执行文件(https://github.com/google/protobuf/releases)   安装gR
一、grpc安装 将 https://github.com/google/go-genproto 放到 $GOPATH/src/google.golang.org/genproto 将 https://github.com/grpc/grpc-go 放到 $GOPATH/src/google.golang.org/grpc 将 https://github.com/golang/t
参考URL: https://segmentfault.com/a/1190000015220713?utm_source=channel-hottest gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, P
我试图在电子应用程序中要求grpc,但我收到以下错误: Error: dlopen(/srv/node_modules/grpc/src/node/extension_binary/grpc_node.node, 1): Symbol not found: _GENERAL_NAME_free Referenced from: /srv/node_modules/grpc/src/node/e
我试图调用GRPC端点,但我想提供客户身份验证标头.我在哪里指定这个? var client = new proto.Publisher('127.0.0.1:50051', grpc.credentials.createInsecure()); var customHeader = { 'authorization': 'secret' } client.publish(d
我正在尝试创建一个 java grpc客户端来与go中的服务器通信.我是grpc的新手所以遵循本教程 gRPC Java Tutorial.在这些示例中,它们指的是阻塞和非阻塞存根,它们似乎是从 github的其他地方导入的. import io.grpc.examples.routeguide.RouteGuideGrpc.RouteGuideBlockingStub; import io.gr
我正在尝试做类似下面的事情(即使用流式grpc调用从客户端向服务器发送数据).代码参考取自官方网站上给出的grpc示例,用于解释目的: 客户端代码: ClientContext context; context.AddMetadata("authorization", "abcd"); context.set_deadline(...); std::unique_ptr<ClientWriter
什么是gRPC gRPC是google开源的一个高性能、跨语言的RPC框架,基于HTTP2协议,采用ProtoBuf 定义的IDL。 gRPC 的主要优点是: 现代高性能轻量级 RPC 框架。 协定优先 API 开发,默认使用协议缓冲区,允许与语言无关的实现。 可用于多种语言的工具,以生成强类型服务器和客户端。 支持客户端、服务器和双向流式处理调用。 使用 Protobuf 二进制序列化减少对网络
一.简介 gRPC 是一个由Google开源的,跨语言的,高性能的远程过程调用(RPC)框架。 gRPC使客户端和服务端应用程序可以透明地进行通信,并简化了连接系统的构建。它使用HTTP/2作为通信协议,使用 Protocol Buffers 作为序列化协议。 它的主要优点: 现代高性能轻量级 RPC 框架。 约定优先的 API 开发,默认使用 Protocol Buffers 作为描述语言,允许
目录 ASP.NET Core 3.0 使用gRPC ASP.NET Core 3.0 gRPC 双向流 ASP.NET Core 3.0 gRPC 认证授权 一.前言 在前一文 《ASP.NET Core 3.0 使用gRPC》中有提到 gRPC 支持双向流调用,支持实时推送消息,这也是 gRPC的一大特点,且 gRPC 在对双向流的控制支持上也是非常强大的。 二. 什么是 gRPC 流 gRP