一、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分布式链路追踪探索实践的更多相关文章

  1. 个推基于 Zipkin 的分布式链路追踪实践

    作者:个推应用平台基础架构高级研发工程师 阿飞   01业务背景   随着微服务架构的流行,系统变得越来越复杂,单体的系统被拆成很多个模块,各个模块通过轻量级的通信协议进行通讯,相互协作,共同实现系统 ...

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

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

  3. 微服务架构学习与思考(09):分布式链路追踪系统-dapper论文学习

    一.技术产生的背景 1.1 背景 先来了解一下分布式链路追踪技术产生的背景. 在现在这个发达的互联网世界,互联网的规模越来越大,比如 google 的搜索,Netflix 的视频流直播,淘宝的购物等. ...

  4. Go微服务框架go-kratos实战05:分布式链路追踪 OpenTelemetry 使用

    一.分布式链路追踪发展简介 1.1 分布式链路追踪介绍 关于分布式链路追踪的介绍,可以查看我前面的文章 微服务架构学习与思考(09):分布式链路追踪系统-dapper论文学习(https://www. ...

  5. 一文详解|Go 分布式链路追踪实现原理

    在分布式.微服务架构下,应用一个请求往往贯穿多个分布式服务,这给应用的故障排查.性能优化带来新的挑战.分布式链路追踪作为解决分布式应用可观测问题的重要技术,愈发成为分布式应用不可缺少的基础设施.本文将 ...

  6. 解读Go分布式链路追踪实现原理

    摘要:本文将详细介绍分布式链路的核心概念.架构原理和相关开源标准协议,并分享我们在实现无侵入 Go 采集 Sdk 方面的一些实践. 本文分享自华为云社区<一文详解|Go 分布式链路追踪实现原理& ...

  7. 基于zipkin分布式链路追踪系统预研第一篇

    本文为博主原创文章,未经博主允许不得转载. 分布式服务追踪系统起源于Google的论文“Dapper, a Large-Scale Distributed Systems Tracing Infras ...

  8. zipkin分布式链路追踪系统

    基于zipkin分布式链路追踪系统预研第一篇   分布式服务追踪系统起源于Google的论文“Dapper, a Large-Scale Distributed Systems Tracing Inf ...

  9. NET Core微服务之路:SkyWalking+SkyApm-dotnet分布式链路追踪系统的分享

    对于普通系统或者服务来说,一般通过打日志来进行埋点,然后再通过elk或splunk进行定位及分析问题,更有甚者直接远程服务器,直接操作查看日志,那么,随着业务越来越复杂,企业应用也进入了分布式服务化的 ...

随机推荐

  1. Vue 使用mixin抽取共通方法

    引入原因: 当一段逻辑在不同的地方使用时 step-1: 定义mixin文件,methods里有一个handleToLink方法 /** * this mixin file will be used ...

  2. Who Am I? Personality Detection based on Deep Learning for Texts 阅读笔记

    文章目录 源代码github地址 摘要 2CLSTM 过程 1. 词嵌入 2. 2LSTM处理 3. CNN学习LSGCNN学习LSG 4. Softmax分类 源代码github地址 https:/ ...

  3. geth建立私链以及发布第一个智能合约

    原博客地址 https://blog.csdn.net/qq_36124194/article/details/83686740 geth建立私链 初始化genesis.json文件 geth --d ...

  4. 聊聊mysql中的int(1)

    昨天有个读者问了我这样一个问题在mysql中建表的时候,我设置一个字段为int类型,长度为1,但是我发现这个字段却可以存储任意长度的数字,这是什么情况?这个问题在我刚接触数据库的时候也遇到过,我觉得有 ...

  5. 简述python中`functools.wrapper()

    简述python中functools.wrapper() 首先对于最简单的函数: def a(): pass if __name__ == '__main__': print(a.__name__) ...

  6. JetCache埋点的骚操作,不服不行啊

    阐述背景 缓存是应对高并发绝对的利器,在很多业务场景允许的情况下,都可以使用缓存来提供性能. 既然用了缓存,那对缓存进行监控必不可少.比如缓存加载耗时,新增耗时等. 在 JetCache 中进行埋点操 ...

  7. 使用BurpSuite、Hydra和medusa爆破相关的服务

    一.基本定义 1.爆破=爆破工具(BP/hydra)+字典(用户字典/密码字典). 字典:就是一些用户名或者口令(弱口令/使用社工软件的生成)的集合. 2.BurpSuite 渗透测试神器,使用Jav ...

  8. VUE 中引入百度地图(vue-Baidu-Map)

    1.安装 $ npm install vue-baidu-map --save 2.全局注册,在main.js中引入以下代码 import BaiduMap from 'vue-baidu-map' ...

  9. CSP-J2019 NOIP普及组初赛真题(阅读程序部分)

    阅读程序(程序输入不超过数组或字符串定义的范围:判断题正确填√,错误填×:除特殊说明外,判断题1.5分,选择题3分,共计40分) #include <cstdio> #include &l ...

  10. cenos 7 mysql

    CentOS 7的yum源中貌似没有正常安装mysql时的mysql-sever文件,需要去官网上下载 # wget http://dev.mysql.com/get/mysql-community- ...