概述

首先同步下项目概况:

上篇文章分享了,路由中间件 - Jaeger 链路追踪(理论篇),这篇文章咱们接着分享:路由中间件 - Jaeger 链路追踪(实战篇)。

这篇文章,确实让大家久等了,主要是里面有一些技术点都是刚刚研究的,没有存货。

先看下咱们要实现的东西:

API 调用了 5 个服务,其中 4 个 gRPC 服务,1 个 HTTP 服务,服务与服务之间又相互调用:

  • Speak 服务,又调用了 Listen 服务 和 Sing 服务。
  • Read 服务,又调用了 Listen 服务 和 Sing 服务。
  • Write 服务,又调用了 Listen 服务 和 Sing 服务。

咱们要实现的就是查看 API 调用的链路。

关于一些理论的东西,大家可以去看看上篇文章或查阅一些资料,这篇文章就是实现怎么用。

OK,开整。

Jaeger 部署

咱们使用 All in one 的方式,进行本地部署。

下载地址:https://www.jaegertracing.io/download/

我的电脑是 macOS 选择 -> Binaries -> macOS

下载后并解压,会发现以下文件:

  • example-hotrod
  • jaeger-agent
  • jaeger-all-in-one
  • jaeger-collector
  • jaeger-ingester
  • jaeger-query

进入到解压后的目录执行:

./jaeger-all-in-one

目测启动后,访问地址:

http://127.0.0.1:16686/

到这,Jaeger 已经部署成功了。

准备测试服务

准备的五个测试服务如下:

听(listen)

  • 端口:9901
  • 通讯:gRPC

说(speak)

  • 端口:9902
  • 通讯:gRPC

读(read)

  • 端口:9903
  • 通讯:gRPC

写(write)

  • 端口:9904
  • 通讯:gRPC

唱(sing)

  • 端口:9905
  • 通讯:HTTP

听、说、读、写、唱,想这几个服务的名称就花了好久 ~

我默认大家都会写 grpc 服务,如果不会写的,可以查看下我原来的文章《Go gRPC Hello World》。

应用示例

实例化 Tracer

func NewJaegerTracer(serviceName string, jaegerHostPort string) (opentracing.Tracer, io.Closer, error) {

	cfg := &jaegerConfig.Configuration {
Sampler: &jaegerConfig.SamplerConfig{
Type : "const", //固定采样
Param : 1, //1=全采样、0=不采样
}, Reporter: &jaegerConfig.ReporterConfig{
LogSpans : true,
LocalAgentHostPort : jaegerHostPort,
}, ServiceName: serviceName,
} tracer, closer, err := cfg.NewTracer(jaegerConfig.Logger(jaeger.StdLogger))
if err != nil {
panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err))
}
opentracing.SetGlobalTracer(tracer)
return tracer, closer, err
}

HTTP 注入

injectErr := jaeger.Tracer.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
if injectErr != nil {
log.Fatalf("%s: Couldn't inject headers", err)
}

HTTP 拦截

spCtx, err := opentracing.GlobalTracer().Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(c.Request.Header))
if err != nil {
ParentSpan = Tracer.StartSpan(c.Request.URL.Path)
defer ParentSpan.Finish()
} else {
ParentSpan = opentracing.StartSpan(
c.Request.URL.Path,
opentracing.ChildOf(spCtx),
opentracing.Tag{Key: string(ext.Component), Value: "HTTP"},
ext.SpanKindRPCServer,
)
defer ParentSpan.Finish()
}

gRPC 注入

func ClientInterceptor(tracer opentracing.Tracer, spanContext opentracing.SpanContext) grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string,
req, reply interface{}, cc *grpc.ClientConn,
invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { span := opentracing.StartSpan(
"call gRPC",
opentracing.ChildOf(spanContext),
opentracing.Tag{Key: string(ext.Component), Value: "gRPC"},
ext.SpanKindRPCClient,
) defer span.Finish() md, ok := metadata.FromOutgoingContext(ctx)
if !ok {
md = metadata.New(nil)
} else {
md = md.Copy()
} err := tracer.Inject(span.Context(), opentracing.TextMap, MDReaderWriter{md})
if err != nil {
span.LogFields(log.String("inject-error", err.Error()))
} newCtx := metadata.NewOutgoingContext(ctx, md)
err = invoker(newCtx, method, req, reply, cc, opts...)
if err != nil {
span.LogFields(log.String("call-error", err.Error()))
}
return err
}
}

gRPC 拦截

func serverInterceptor(tracer opentracing.Tracer) grpc.UnaryServerInterceptor {
return func(ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (resp interface{}, err error) { md, ok := metadata.FromIncomingContext(ctx)
if !ok {
md = metadata.New(nil)
} spanContext, err := tracer.Extract(opentracing.TextMap, MDReaderWriter{md})
if err != nil && err != opentracing.ErrSpanContextNotFound {
grpclog.Errorf("extract from metadata err: %v", err)
} else {
span := tracer.StartSpan(
info.FullMethod,
ext.RPCServerOption(spanContext),
opentracing.Tag{Key: string(ext.Component), Value: "gRPC"},
ext.SpanKindRPCServer,
)
defer span.Finish() ParentContext = opentracing.ContextWithSpan(ctx, span)
} return handler(ParentContext, req)
}
}

上面是一些核心的代码,涉及到的全部代码我都会上传到 github,供下载。

运行

启动服务

// 启动 Listen 服务
cd listen && go run main.go // 启动 Speak 服务
cd speak && go run main.go // 启动 Read 服务
cd read && go run main.go // 启动 Write 服务
cd write && go run main.go // 启动 Sing 服务
cd sing && go run main.go // 启动 go-gin-api 服务
cd go-gin-api && go run main.go

访问路由

http://127.0.0.1:9999/jaeger_test

效果

就到这吧。

API 源码地址

https://github.com/xinliangnote/go-gin-api

Service 源码地址

https://github.com/xinliangnote/go-jaeger-demo

go-gin-api 系列文章

[系列] go-gin-api 路由中间件 - Jaeger 链路追踪(六)的更多相关文章

  1. [系列] go-gin-api 路由中间件 - Jaeger 链路追踪(五)

    概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - 捕获异常,这篇文章咱们分享:路由中间件 - Jaeger 链路追踪. 啥是链路追踪? 我理解链路追踪其实是为微服务架构提供服务的,当一个请求 ...

  2. go-gin-api 路由中间件 - Jaeger 链路追踪

    概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - Jaeger 链路追踪(理论篇). 这篇文章咱们分享:路由中间件 - Jaeger 链路追踪(实战篇). 说实话,这篇文章确实让大家久等了, ...

  3. go-zero docker-compose 搭建课件服务(八):集成jaeger链路追踪

    0.转载 go-zero docker-compose 搭建课件服务(八):集成jaeger链路追踪 0.1源码地址 https://github.com/liuyuede123/go-zero-co ...

  4. spring cloud 系列第7篇 —— sleuth+zipkin 服务链路追踪 (F版本)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.简介 在微服务架构中,几乎每一个前端的请求都会经过多个服务单元协调来提 ...

  5. [系列] go-gin-api 路由中间件 - 签名验证(七)

    目录 概览 MD5 组合 AES 对称加密 RSA 非对称加密 如何调用? 性能测试 PHP 与 Go 加密方法如何互通? 源码地址 go-gin-api 系列文章 概览 首先同步下项目概况: 上篇文 ...

  6. go-gin-api 路由中间件 - 签名验证(七)

    概览 首先同步下项目概况: 上篇文章分享了,路由中间件 - Jaeger 链路追踪(实战篇),文章反响真是出乎意料, 「Go中国」 公众号也转发了,有很多朋友加我好友交流,直呼我大神,其实我哪是什么大 ...

  7. 微服务从代码到k8s部署应有尽有系列(十二、链路追踪)

    我们用一个系列来讲解从需求到上线.从代码到k8s部署.从日志到监控等各个方面的微服务完整实践. 整个项目使用了go-zero开发的微服务,基本包含了go-zero以及相关go-zero作者开发的一些中 ...

  8. 带你十天轻松搞定 Go 微服务系列(九、链路追踪)

    序言 我们通过一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下: 环境搭建 服务拆分 用户服务 产品服务 订单服务 支付服务 RPC 服务 Auth 验证 ...

  9. .NET Core 中的日志与分布式链路追踪

    目录 .NET Core 中的日志与分布式链路追踪 .NET Core 中的日志 控制台输出 非侵入式日志 Microsoft.Extensions.Logging ILoggerFactory IL ...

随机推荐

  1. Ubuntu 18.04 下载地址

    http://mirrors.163.com/ubuntu-releases/18.04/

  2. 解决ionic 上拉加载组件 ion-infinite-scroll自动调用多次的问题

    ionic 中一个上拉刷新的组件 ion-infinite-scroll,如果页面未填充满页面高度,会自动检测并无限调用多次加载更多的函数:当然,主要会导致首次调用的时候,会执行几次加载更多的函数: ...

  3. JavaScript清除空格、换行,把双引号转换成单引号

    1.页面 2.源码 <!DOCTYPE> <html> <head> <meta charset="utf-8"> <titl ...

  4. Python--函数参数类型、用法及代码示例

    在编程语言里,将一个个功能定义成函数,能够进行反复调用,而不是每次都重复相同的代码,这种方式能够大幅度降低代码的复杂度. 函数的好处: 1.代码重用 2.保持一致性 3.可扩展性 1.基础 我们定义函 ...

  5. Django之使用中间件解决前后端同源策略问题

    问题描述 前端时间在公司的时候,要使用angular开发一个网站,因为angular很适合前后端分离,所以就做了一个简单的图书管理系统来模拟前后端分离. 但是在开发过程中遇见了同源策略的跨域问题,页面 ...

  6. Chrome 开发工具之 Application

    Chrome 开发者工具有 Application 这么一个面板,主要作用是检查 web 应用加载的所有资源,包括 Manifest.Service Workers.Local Storage.Ses ...

  7. Oracle中的一些基本sql语句

    --新建表create table table1 (id varchar(300) primary key,name varchar(200) not null);--插入数据insert into ...

  8. Java开发必备技能

    --------转载自B站up主 codeSheep 基础知识 编程语言:Java Python C 基本算法 基本网络知识:TCP/IP  HTTP/HTTPS 基本的设计模式 工具方面 操作系统: ...

  9. vue路由菜单权限设置就button权限设置

    路由权限的设计思路: 首先,我们的需要校验权限的路由的 url,全部由后端返回,后端会返回当前用户的路由树数组.前端在进入页面前请求接口,把数据拿到: 其次,前端会维护一个路由映射组件的列表,如果路由 ...

  10. python控制窗口口字形运动

    import win32con import win32gui import time import math notepad = win32gui.FindWindow("Photo_Li ...