opentracting+jager分布式链路追踪探索实践
一、Opentracing
opentracing通过提供平台无关、厂商无关的API,使得开发人员可以方便地实现追踪系统。opentracing提供了用于运营支撑系统和针对特定平台的辅助程序库,被跟踪的服务只需要调用这套接口,就可以被任何实现这套接口的跟踪后台(比如Zipkin, Jaeger等等)支持,而作为一个跟踪后台,只要实现了个这套接口,就可以跟踪到任何调用这套接口的服务。
二、Jaeger
Jaeger是Uber开源基于golang的分布式跟踪系统,使用Jaeger可以非常直观的展示整个分布式系统的调用链,由此可以很好发现和解决问题。
Jaeger的整体架构如下:

Jaeger组件
jaeger-agent
jaeger-agent是一个网络守护进程,监听通过UDP发送过来的Span,它会将其批量发送给collector。按照设计,Agent要被部署到所有主机上,作为基础设施。Agent将collector和客户端之间的路由与发现机制抽象出来。
jaeger-collector
jaeger-collector从Jaeger Agent接收Trace,并通过一个处理管道对其进行处理。目前的管道会校验Trace、建立索引、执行转换并最终进行存储。存储是一个可插入的组件,目前支持Cassandra和elasticsearch。
jaeger-query
jaeger-query服务会从存储中检索Trace并通过UI界面进行展现,通过UI界面可以展现Trace的详细信息。
三、 安装使用
1.下载安装jaeger
https://www.jaegertracing.io/download/
下载并解压
增加执行权限:
chmod a+x jaeger-*
2. 安装golang package
go get github.com/opentracing/opentracing-go
go get github.com/uber/jaeger-client-go
3. 指定用ES存储
export SPAN_STORAGE_TYPE=elasticsearch
export ES_SERVER_URLS=http://10.20.xx.xx:9200
4. 运行jarger
cd jaeger-1.18.0-linux-amd64
./jaeger-all-in-one

在浏览器输入http://10.20.xx.xx:16686/即可打开jaeger页面:

使用docker运行jaeger的各组件:
docker-compose.yaml
version: '3'
services:
jaeger-agent:
image: jaegertracing/jaeger-agent:1.18
stdin_open: true
tty: true
links:
- jaeger-collector:jaeger-collector
ports:
- 6831:6831/udp
command:
- --reporter.grpc.host-port=jaeger-collector:14250
jaeger-collector:
image: jaegertracing/jaeger-collector:1.18
environment:
SPAN_STORAGE_TYPE: elasticsearch
ES_SERVER_URLS: http://10.20.xx.xx:9200
stdin_open: true
tty: true
jaeger-query:
image: jaegertracing/jaeger-query:1.18
environment:
SPAN_STORAGE_TYPE: elasticsearch
ES_SERVER_URLS: http://10.20.xx.xx:9200
stdin_open: true
tty: true
ports:
- 16686:16686/tcp
四、 在gin中使用jaeger
以wire依赖注入的方式引入jaeger:
package jaeger import (
"fmt"
"github.com/google/wire"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/uber/jaeger-client-go"
"github.com/uber/jaeger-client-go/config"
"go.uber.org/zap"
"io"
) // ClientType 定义jaeger client 结构体
type ClientType struct {
Tracer opentracing.Tracer
Closer io.Closer
} // Client jaeger连接类型
var Client ClientType // Options jaeger option
type Options struct {
Type string // const
Param float64 // 1
LogSpans bool // true
LocalAgentHostPort string // host:port
Service string // service name
} // NewOptions for jaeger
func NewOptions(v *viper.Viper, logger *zap.Logger) (*Options, error) {
var (
err error
o = new(Options)
)
if err = v.UnmarshalKey("jaeger", o); err != nil {
return nil, errors.Wrap(err, "unmarshal redis option error")
} logger.Info("load jaeger options success", zap.Any("jaeger options", o))
return o, err
} // New returns an instance of Jaeger Tracer that samples 100% of traces and logs all spans to stdout.
func New(o *Options) (opentracing.Tracer, error) {
cfg := &config.Configuration{
Sampler: &config.SamplerConfig{
Type: o.Type,
Param: o.Param,
},
Reporter: &config.ReporterConfig{
LogSpans: o.LogSpans,
// 注意:填下地址不能加http://
LocalAgentHostPort: o.LocalAgentHostPort,
},
}
tracer, closer, err := cfg.New(o.service, config.Logger(jaeger.StdLogger))
if err != nil {
panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err))
}
Client.Tracer = tracer
Client.Closer = closer return tracer, err
} // ProviderSet inject jaeger settings
var ProviderSet = wire.NewSet(New, NewOptions)
初始化全局jaeger客户端,调用jaeger
// Article 发布文章
func (pc *ArticleController) Article(c *gin.Context) {
var req requests.Article
if err := c.ShouldBind(&req); err != nil {
pc.logger.Error("参数错误", zap.Error(err))
c.JSON(http.StatusBadRequest, httputil.Error(nil, "参数校验失败"))
return
}
tracer := jaeger.Client.Tracer
opentracing.SetGlobalTracer(tracer)
span := tracer.StartSpan("Article")
defer span.Finish()
ctx := context.Background()
ctx = opentracing.ContextWithSpan(ctx, span)
span.SetTag("http.url", c.Request.URL.Path)
article, err := pc.service.Article(ctx, &req)
if err != nil {
pc.logger.Error("发表文章失败", zap.Error(err))
c.JSON(http.StatusInternalServerError, httputil.Error(nil, "发表主题失败"))
return
}
span.LogFields(
log.String("event", "info"),
log.Int("article", article.ID),
)
c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "success", "data": article})
}
通过span和context传递tracer
// Article 发表文章
func (s *DefaultArticleService) Article(ctx context.Context, req *requests.Article) (responses.Article, error) {
var result responses.Article
cateInfo := s.Repository.GetCategory(req.CategoryID)
if cateInfo.ID == 0 {
s.logger.Error("categoryId 不存在", zap.Any("category_id", req.CategoryID))
return result, gorm.ErrRecordNotFound
}
span, _ := opentracing.StartSpanFromContext(ctx, "Article")
defer span.Finish()
span.LogFields(
log.String("event", "insert article service"),
) res, err := s.Repository.Article(req) if err != nil {
s.logger.Error("发表文章失败", zap.Error(err))
return result, errors.New("发表文章失败")
} result = responses.Article{
ID: res.ID,
Title: res.Title,
}
return result, err
}
jaeger调用详情:

五、 在go micro中使用jaeger
go micro中各rpc服务之间的调用定位异常较为困难,可以在网关处初始化jaeger,通过传递context的方式记录追踪调用的service
具体代码实现如下:
func initJaeger(service string) (opentracing.Tracer, io.Closer) {
cfg := &jaegercfg.Configuration{
Sampler: &jaegercfg.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jaegercfg.ReporterConfig{
LogSpans: true,
// 注意:填下地址不能加http:
LocalAgentHostPort: "192.168.33.16:6831",
},
}
tracer, closer, err := cfg.New(service, jaegercfg.Logger(jaeger.StdLogger))
if err != nil {
panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err))
}
return tracer, closer
}
func main() {
userRpcFlag := cli.StringFlag{
Name: "f",
Value: "./config/config_api.json",
Usage: "please use xxx -f config_rpc.json",
}
configFile := flag.String(userRpcFlag.Name, userRpcFlag.Value, userRpcFlag.Usage)
flag.Parse()
conf := new(gateWayConfig.ApiConfig)
if err := config.LoadFile(*configFile); err != nil {
log.Fatal(err)
}
if err := config.Scan(conf); err != nil {
log.Fatal(err)
}
tracer, _ := initJaeger("micro-message-system.gateway")
opentracing.SetGlobalTracer(tracer)
engineGateWay, err := gorm.Open(conf.Engine.Name, conf.Engine.DataSource)
if err != nil {
log.Fatal(err)
}
etcdRegisty := etcdv3.NewRegistry(
func(options *registry.Options) {
options.Addrs = conf.Etcd.Address
});
// Create a new service. Optionally include some options here.
rpcService := micro.NewService(
micro.Name(conf.Server.Name),
micro.Registry(etcdRegisty),
micro.Transport(grpc.NewTransport()),
micro.WrapClient(
hystrix.NewClientWrapper(),
wrapperTrace.NewClientWrapper(tracer),
), // 客户端熔断、链路追踪
micro.WrapHandler(wrapperTrace.NewHandlerWrapper(tracer)),
micro.Flags(userRpcFlag),
)
rpcService.Init()
// 创建用户服务客户端 直接可以通过它调用user rpc的服务
userRpcModel := userProto.NewUserService(conf.UserRpcServer.ServerName, rpcService.Client())
// 创建IM服务客户端 直接可以通过它调用im prc的服务
imRpcModel := imProto.NewImService(conf.ImRpcServer.ServerName, rpcService.Client())
gateWayModel := models.NewGateWayModel(engineGateWay)
// 把用户服务客户端、IM服务客户端 注册到网关
gateLogic := logic.NewGateWayLogic(userRpcModel, gateWayModel, conf.ImRpcServer.ImServerList, imRpcModel)
gateWayController := controller.NewGateController(gateLogic)
// web.NewService会在启动web server的同时将rpc服务注册进去
service := web.NewService(
web.Name(conf.Server.Name),
web.Registry(etcdRegisty),
web.Version(conf.Version),
web.Flags(userRpcFlag),
web.Address(conf.Port),
)
router := gin.Default()
userRouterGroup := router.Group("/gateway")
// 中间件验证
userRouterGroup.Use(middleware.ValidAccessToken)
{
userRouterGroup.POST("/send", gateWayController.SendHandle)
userRouterGroup.POST("/address", gateWayController.GetServerAddressHandle)
}
service.Handle("/", router)
if err := service.Run(); err != nil {
log.Fatal(err)
}
}
调用效果如下:

opentracting+jager分布式链路追踪探索实践的更多相关文章
- 个推基于 Zipkin 的分布式链路追踪实践
作者:个推应用平台基础架构高级研发工程师 阿飞 01业务背景 随着微服务架构的流行,系统变得越来越复杂,单体的系统被拆成很多个模块,各个模块通过轻量级的通信协议进行通讯,相互协作,共同实现系统 ...
- .NET Core 中的日志与分布式链路追踪
目录 .NET Core 中的日志与分布式链路追踪 .NET Core 中的日志 控制台输出 非侵入式日志 Microsoft.Extensions.Logging ILoggerFactory IL ...
- 微服务架构学习与思考(09):分布式链路追踪系统-dapper论文学习
一.技术产生的背景 1.1 背景 先来了解一下分布式链路追踪技术产生的背景. 在现在这个发达的互联网世界,互联网的规模越来越大,比如 google 的搜索,Netflix 的视频流直播,淘宝的购物等. ...
- Go微服务框架go-kratos实战05:分布式链路追踪 OpenTelemetry 使用
一.分布式链路追踪发展简介 1.1 分布式链路追踪介绍 关于分布式链路追踪的介绍,可以查看我前面的文章 微服务架构学习与思考(09):分布式链路追踪系统-dapper论文学习(https://www. ...
- 一文详解|Go 分布式链路追踪实现原理
在分布式.微服务架构下,应用一个请求往往贯穿多个分布式服务,这给应用的故障排查.性能优化带来新的挑战.分布式链路追踪作为解决分布式应用可观测问题的重要技术,愈发成为分布式应用不可缺少的基础设施.本文将 ...
- 解读Go分布式链路追踪实现原理
摘要:本文将详细介绍分布式链路的核心概念.架构原理和相关开源标准协议,并分享我们在实现无侵入 Go 采集 Sdk 方面的一些实践. 本文分享自华为云社区<一文详解|Go 分布式链路追踪实现原理& ...
- 基于zipkin分布式链路追踪系统预研第一篇
本文为博主原创文章,未经博主允许不得转载. 分布式服务追踪系统起源于Google的论文“Dapper, a Large-Scale Distributed Systems Tracing Infras ...
- zipkin分布式链路追踪系统
基于zipkin分布式链路追踪系统预研第一篇 分布式服务追踪系统起源于Google的论文“Dapper, a Large-Scale Distributed Systems Tracing Inf ...
- NET Core微服务之路:SkyWalking+SkyApm-dotnet分布式链路追踪系统的分享
对于普通系统或者服务来说,一般通过打日志来进行埋点,然后再通过elk或splunk进行定位及分析问题,更有甚者直接远程服务器,直接操作查看日志,那么,随着业务越来越复杂,企业应用也进入了分布式服务化的 ...
随机推荐
- 从系统报表页面导出20w条数据到本地只用了4秒,我是如何做到的
背景 最近有个学弟找到我,跟我描述了以下场景: 他们公司内部管理系统上有很多报表,报表数据都有分页显示,浏览的时候速度还可以.但是每个报表在导出时间窗口稍微大一点的数据时,就异常缓慢,有时候多人一起导 ...
- 【算法•日更•第十期】树型动态规划&区间动态规划:加分二叉树题解
废话不多说,直接上题: 1580:加分二叉树 时间限制: 1000 ms 内存限制: 524288 KB提交数: 121 通过数: 91 [题目描述] 原题来自:NOIP 20 ...
- python爬虫抖音 个人资料 仅供学习参考 切勿用于商业
本文仅供学习参考 切勿用于商业 本次爬取使用fiddler+模拟器(下载抖音APP)+pycharm 1. 下载最新版本的fiddler(自行百度下载),以及相关配置 1.1.依次点击,菜单栏-Too ...
- unity探索者之获取设备当前电量
很多入坑了的小伙伴应该都知道,很多时候做移动端项目都会有显示当前电池电量的需求 对于这个功能,在unity中要做的事并不多,核心方法就一个,自己做一个定时器或者直接使用协程间隔调用该方法就好了 pub ...
- CSP-J2019 把8个同样的球放在同样的5个袋子里,允许有的袋子空着不放,问共有多少种不同的分法?
把8个同样的球放在同样的5个袋子里,允许有的袋子空着不放,问共有多少种不同的分法? 提示:如果8个球都放在一个袋子里,无论是放哪个袋子,都只算同一种分法. 解析: 把问题合成,先思索5个袋子都不空的状 ...
- java多线程之消费生产模型
需求:要求仓库最大容量为4,且一共只生产20台电视机,下面的代码只适用于一个生产者一个消费者,有没有大佬提点建议怎么改成一对多或多对多不会出现死锁情况 class Warehouse { privat ...
- go thrift demo
接口:https://gowalker.org/github.com/apache/thrift/lib/go/thrift 参考文件:https://cong.im/2018/05/14/other ...
- Linux下执行SQL文件
最近在使用MySQL数据库时,想要执行一些sql文件,就想到了source命令. source介绍:source命令也称为“点命令”,也就是一个点符号(.),是bash的内部命令. 功能:使Shell ...
- SpringBoot整合Redis并完成工具类
SpringBoot整合Redis的资料很多,但是我只需要整合完成后,可以操作Redis就可以了,所以不需要配合缓存相关的注解使用(如@Cacheable),而且我的系统框架用的日志是log4j,不是 ...
- 第2篇scrum冲刺(5.22)
一.站立会议 1.照片 2.工作安排 成员 昨天完成工作 今日计划工作 困难 陈芝敏 写第一篇scrum 博客,调整工作 计划,学习接口使用 调用小程序接口获取用户微信登录权限,初始化 加载倒计时慢, ...