jaeger分布式链路追踪
推荐去官网系统的了解一下 https://www.jaegertracing.io
随着应用容器化和微服务的兴起,借由Docker和 Kubernetes 等工具, 服务的快速开发和部署成为可能,构建微服务应用变得越来越简单。但是随着大型单体应用拆分为微服务,服务之间的依赖和调用变得极为复杂,这些服务可能是不同团队开发的,可能基于不同的语言,微服务之间可能是利用RPC, RESTful API, 也可能是通过消息队列实现调用或通讯。如何理清服务依赖调用关系,如何在这样的环境下快速debug, 追踪服务处理耗时,查找服务性能瓶颈, 合理对服务的容量评估都变成一个棘手的事情。
Tracing在微服务中的作用
和传统单体服务不同, 微服务通常部署在一个分布式的系统中, 并且一个请求可能会经过好几个微服务的处理, 这样的环境下错误和性能问题就会更容易发生, 所以观察(Observe)尤为重要,
这就是Tracing的用武之地, 它收集调用过程中的信息并可视化, 让你知道在每一个服务调用过程的耗时等情况, 以便及早发现问题.
在上图可以看到api层一共花了4.03s, 然后其中调用其他服务: 'service-1'花了2.12s, 而service-1又调用了'service-2'花费了2.12s, 用这样的图示很容易就能排查到系统存在的问题. 在这里我只展示了时间, 如果需要追踪其他信息(如错误信息)也是可以实现的.
为什么是Jaeger
支持 OpenTracing
的 server
端有很多,我们总要选一个 。在这里,选用 jaeger
。 jaeger
的开发较为活跃,支持的客户端实现也较多。由于采用了 golang
开发,发行包也比较简洁。
jaeger的官网是 www.jaegertracing.io/
特点
- jaeger的开发语言是
golang
- jaeger支持OpenTracing协议,同属于CNCF基金会
- jaeger支持各种各样的客户端,包括Go、Java、Node、Python、C++等
- jaeger支持udp协议传输,当然也支持http
jaeger能够解决以下问题
- 分布式事务监控
- 性能分析与性能优化
- 调用链,找到根源问题
- 服务依赖分析(需大数据分析)
安装需了解的技术栈:
- OpenTracing
- Golang
- ElasticSearch
- Kafka (可选)
OpenTracing 标准
云原生基金会(CNCF) 推出了 OpenTracing 标准,推进Tracing协议和工具的标准化, 统一 Trace 数据结构和格式。 OpenTracing 通过提供平台无关、厂商无关的 API,使得开发人员能够方便的添加(或更换)追踪系统的实现。比如从Zipkin替换成Jaeger/Skywalking等后端。
在OpenTracing中,有两个主要概念:
1、Trace(调用链): OpenTracing中的Trace(调用链)通过归属于此调用链的Span来隐性的定义。一条Trace(调用链)可以被认为是一个由多个Span组成的有向无环图(DAG图), Span与Span的关系被命名为References。
2、Span(跨度):可以被理解为一次方法调用, 一个程序块的调用, 或者一次RPC/数据库访问. 只要是一个具有完整时间周期的程序访问,都可以被认为是一个Span。
单个Trace中,Span间的因果关系如下图:
这里使用目前比较流行的Tracing开源方案Jaeger进行实践,使用jaeger-client-go这个库作为client
github地址:GitHub - jaegertracing/jaeger-client-go: Jaeger Bindings for Go OpenTracing API.
开放分布式追踪(OpenTracing)入门与 Jaeger 实现
jaeger架构
jaeger组件介绍:
jaeger-client:jaeger 的客户端,实现了opentracing协议;
jaeger-agent:jaeger client的一个代理程序,client将收集到的调用链数据发给agent,然后由agent发给collector;
jaeger-collector:负责接收jaeger client或者jaeger agent上报上来的调用链数据,然后做一些校验,比如时间范围是否合法等,最终会经过内部的处理存储到后端存储;
jaeger-query:专门负责调用链查询的一个服务,有自己独立的UI;
jaeger-ingester:中文名称“摄食者”,可用从kafka读取数据然后写到jaeger的后端存储,比如Cassandra和Elasticsearch;
spark-job:基于spark的运算任务,可以计算服务的依赖关系,调用次数等;
其中jaeger-collector和jaeger-query是必须的,其余的都是可选的,我们没有采用agent上报的方式,而是让客户端直接通过endpoint上报到collector。
官方文档的demo:example
首先,本地起一个jaeger服务作为测试用的服务端,官方提供了”All in One”的docker镜像, 启动Jaeger服务只需要一行代码:
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.12
本人使用下载好的golang二进制文件启动的,jaeger官网地址:https://www.jaegertracing.io/downlo
jaeger的二进制发行包包含五个二进制文件:
- jaeger-agent
- jaeger-collector
- jaeger-query
- jaeger-standalone
- jaeger-ingester
如果没有执行权限,可以使用
- chmod a+x jaeger-*
选择存储
trace数据总要存在一个地方。jaeger支持 ES
和 Canssandra
两种后端DB。国内用ES的多一点,我们就以ES为例,来介绍其安装方式。
ES请先自行安装。
由于上面四个命令都有很多参数,所以我们可以创建几个脚本,来支持jaeger的启动。
start-collector.sh
- export SPAN_STORAGE_TYPE=elasticsearch
- nohup ./jaeger-collector --es.server-urls http://10.66.177.152:9200/ --log-level=debug > collector.log 2>&1 &
start-agent.sh
- export SPAN_STORAGE_TYPE=elasticsearch
- nohup .\jaeger-agent.exe --reporter.grpc.host-port=192.168.1.234:14250 --log-level=debug > agent.log 2>&1 &
start-query.sh
- export SPAN_STORAGE_TYPE=elasticsearch
- nohup ./jaeger-query --span-storage.type=elasticsearch --es.server-urls=http://10.66.177.152:9200/ > query.log 2>&1 &
部署方式
jaeger有两种部署方式。下面一一介绍。如果你的数据量特别多,使用kafka缓冲一下也是可以的(所以就引入了另外一个组件jaeger-ingester),不多做介绍。
简易环境
这种方式一般用在dev环境或者其他测试环境。只需要部署一个单一节点即可。我们的app,需要手动填写agent的地址,这个地址一般都是固定的。
这些环境的流量很小,一个agent是足够的。
生产环境
上面这种部署方式,适合生产环境。agent安装在每一台业务机器上。Client端的目标agent只需要填写localhost即可。
这种方式的好处是生产环境的配置非常的简单。即使你的机器是混合部署的,也能正常收集trace信息。
调用关系图
jaeger的调用关系图是使用spark任务进行计算的。项目地址为:
https://github.com/jaegertracing/spark-dependencies
端口整理
Agent
- 5775 UDP协议,接收兼容zipkin的协议数据
- 6831 UDP协议,接收兼容jaeger的兼容协议
- 6832 UDP协议,接收jaeger的二进制协议
- 5778 HTTP协议,数据量大不建议使用
它们之间的传输协议都是基于thrift封装的。我们默认使用5775作为传输端口
Collector
- 14267 tcp agent发送jaeger.thrift格式数据
- 14250 tcp agent发送proto格式数据(背后gRPC)
- 14268 http 直接接受客户端数据
- 14269 http 健康检查
Query
- 16686 http jaeger的前端,放给用户的接口
- 16687 http 健康检查
至此,我们的jaeger就安装完毕。
以上,就是我们的环境准备。有了一个server接收数据,调用链的主要工作就在于客户端开发
接下来,代码时间, 参考项目的Readme和搜索引擎不难写出以下代码
1,最简单的使用模式
2,多个函数之间调用
3,http请求
clinet中同步请求:8081/format,:8088/publish
package mainimport ("fmt""context""github.com/opentracing/opentracing-go/ext""io/ioutil""net/http"opentracing "github.com/opentracing/opentracing-go"jaeger "github.com/uber/jaeger-client-go"config "github.com/uber/jaeger-client-go/config""github.com/opentracing/opentracing-go/log""io""net/url"
)// Init returns an instance of Jaeger Tracer that samples 100% of traces and logs all spans to stdout.
func initJaeger(service string) (opentracing.Tracer, io.Closer) {cfg := &config.Configuration{Sampler:&config.SamplerConfig{Type: "const",Param:1,},Reporter: &config.ReporterConfig{LogSpans: true,//LocalAgentHostPort: "192.168.1.234:6831",LocalAgentHostPort: "192.168.2.246:6831",},}tracer, closer, err := cfg.New(service, config.Logger(jaeger.StdLogger))if err != nil {panic(fmt.Sprintf("Error: connot init Jaeger: %v\n", err))}return tracer, closer
}func main() {tracer, closer := initJaeger("http-demo-client")defer closer.Close()opentracing.SetGlobalTracer(tracer)span := tracer.StartSpan("say-hello")span.SetTag("hello-to", "helloTo")defer span.Finish()ctx := opentracing.ContextWithSpan(context.Background(), span)helloStr := formatString(ctx, "helloTo")printHello(ctx, helloStr)fmt.Println("exit")}func formatString(ctx context.Context, helloTo string) string {span, _ := opentracing.StartSpanFromContext(ctx, "formatString")defer span.Finish()v := url.Values{}v.Set("helloTo", helloTo)url := "http://localhost:8081/format?" + v.Encode()req, err := http.NewRequest("GET", url, nil)if err != nil {panic(err.Error())}ext.SpanKindRPCClient.Set(span)ext.HTTPUrl.Set(span, url)ext.HTTPMethod.Set(span, "GET")span.Tracer().Inject(span.Context(),opentracing.HTTPHeaders,opentracing.HTTPHeadersCarrier(req.Header),)resp, err := httpDo(req)if err != nil {panic(err.Error())}helloStr := string(resp)span.LogFields(log.String("event", "string-format"),log.String("value", helloStr),)return helloStr
}func printHello(ctx context.Context, helloStr string) {span, _ := opentracing.StartSpanFromContext(ctx, "printHello")defer span.Finish()v := url.Values{}v.Set("helloStr", helloStr)url := "http://localhost:8088/publish?" + v.Encode()req, err := http.NewRequest("GET", url, nil)if err != nil {panic(err.Error())}ext.SpanKindRPCClient.Set(span)ext.HTTPUrl.Set(span, url)ext.HTTPMethod.Set(span, "GET")span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))if _, err := httpDo(req); err != nil {panic(err.Error())}
}func httpDo(req *http.Request) ([]byte, error) {resp, err := http.DefaultClient.Do(req)if err != nil {return nil, err}defer resp.Body.Close()body, err := ioutil.ReadAll(resp.Body)if err != nil {return nil, err}if resp.StatusCode != 200 {return nil, fmt.Errorf("StatusCode: %d, Body: %s", resp.StatusCode, body)}return body, nil
}
package mainimport ("fmt""io""log""net/http"opentracing "github.com/opentracing/opentracing-go""github.com/opentracing/opentracing-go/ext"otlog "github.com/opentracing/opentracing-go/log"jaeger "github.com/uber/jaeger-client-go"config "github.com/uber/jaeger-client-go/config"
)// Init returns an instance of Jaeger Tracer that samples 100% of traces and logs all spans to stdout.
func initJaeger(service string) (opentracing.Tracer, io.Closer) {cfg := &config.Configuration{Sampler:&config.SamplerConfig{Type: "const",Param:1,},Reporter: &config.ReporterConfig{LogSpans: true,//LocalAgentHostPort: "192.168.1.234:6831",LocalAgentHostPort: "192.168.2.246:6831",},}tracer, closer, err := cfg.New(service, config.Logger(jaeger.StdLogger))if err != nil {panic(fmt.Sprintf("Error: connot init Jaeger: %v\n", err))}return tracer, closer
}func main() {tracer, closer := initJaeger("http-formatter")defer closer.Close()http.HandleFunc("/format", func(w http.ResponseWriter, r *http.Request) {spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))span := tracer.StartSpan("format", ext.RPCServerOption(spanCtx))defer span.Finish()helloTo := r.FormValue("helloTo")helloStr := fmt.Sprintf("Hello, I am format %s!", helloTo)span.LogFields(otlog.String("event", "string-format"),otlog.String("value", helloStr),)w.Write([]byte(helloStr))})log.Fatal(http.ListenAndServe(":8081", nil))
}formater/main.go
package mainimport ("fmt""io""log""net/http"opentracing "github.com/opentracing/opentracing-go""github.com/opentracing/opentracing-go/ext"otlog "github.com/opentracing/opentracing-go/log"jaeger "github.com/uber/jaeger-client-go"config "github.com/uber/jaeger-client-go/config"
)// Init returns an instance of Jaeger Tracer that samples 100% of traces and logs all spans to stdout.
func initJaeger(service string) (opentracing.Tracer, io.Closer) {cfg := &config.Configuration{Sampler:&config.SamplerConfig{Type: "const",Param:1,},Reporter: &config.ReporterConfig{LogSpans: true,//LocalAgentHostPort: "192.168.1.234:6831",LocalAgentHostPort: "192.168.2.246:6831",},}tracer, closer, err := cfg.New(service, config.Logger(jaeger.StdLogger))if err != nil {panic(fmt.Sprintf("Error: connot init Jaeger: %v\n", err))}return tracer, closer
}func main() {tracer, closer := initJaeger("http-publish")defer closer.Close()http.HandleFunc("/publish", func(w http.ResponseWriter, r *http.Request) {spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))span := tracer.StartSpan("format", ext.RPCServerOption(spanCtx))defer span.Finish()helloTo := r.FormValue("helloTo")helloStr := fmt.Sprintf("Hello, I am publish %s!", helloTo)span.LogFields(otlog.String("event", "string-publish"),otlog.String("value", helloStr),)w.Write([]byte(helloStr))})log.Fatal(http.ListenAndServe(":8088", nil))
}publish/main.go
jaeger分布式链路追踪相关推荐
- go 链路追踪_【go-micro实践】jaeger分布式链路追踪
安装jaeger jaeger提供一个all in one 的docker镜像,可以快速搭建实验环境 docker run -d --name jaeger -e COLLECTOR_ZIPKIN_H ...
- 原来10张图就可以搞懂分布式链路追踪系统原理
分布式系统为什么需要链路追踪? 随着互联网业务快速扩展,软件架构也日益变得复杂,为了适应海量用户高并发请求,系统中越来越多的组件开始走向分布式化,如单体架构拆分为微服务.服务内缓存变为分布式缓存.服务 ...
- 分布式链路追踪框架的基本实现原理
目录 分布式追踪 分布式系统 分布式追踪 分布式追踪有什么用呢 什么是分布式追踪 Dapper 分布式追踪系统的实现 跟踪树和 span Jaeger 和 OpenTracing OpenTracin ...
- 微服务治理之分布式链路追踪--3.zipkin实战
微服务治理之分布式链路追踪–3.zipkin实战 本节是基于zipkin分布式追踪系统搭建,因为对 scala 和 play framework 2 框架不熟悉,所以,没有采用opentelemetr ...
- 怎么理解分布式链路追踪技术?
▲ 点击上方"分布式实验室"关注公众号 回复"1"抽取纸质技术书 - 1 - 为什么需要链路追踪 在学习分布式链路追踪之前,我们需要先理解这项技术产生的背景,以 ...
- 如何理解分布式链路追踪技术
什么是链路追踪?微服务引发了怎样的问题? 在深入了解分布式链路追踪技术之前,我们需要先理解这项技术产生的背景,以及它能够帮我们解决哪些棘手的问题. 提到分布式链路追踪,我们要先提到微服务.相信很多人都 ...
- 分布式链路追踪之SkyWalking
一 链路追踪简介 在微服务架构中,一次请求往往涉及到多个模块,多个中间件,多台机器的相互协作才能完成.这一系列调用请求中,有些是串行的,有些是并行的,那么如何确定这个请求背后调用了哪些应用,哪些模 ...
- 分布式链路追踪-skywalking入门体验
背景 旁友,你的线上服务是不是偶尔来个超时,或者突然抖动一下,造成用户一堆反馈投诉.然后你费了九牛二虎之力,查了一圈圈代码和日志才总算定位到问题原因了.或者公司内部有链路追踪系统,虽然可以很轻松地通过 ...
- PHP分布式链路追踪,SkyWalking:分布式架构链路追踪-SkyWalking介绍
前面几篇文章提到了微服务相关系统的使用与搭建,在微服务架构下的问题也比较突出.正常系统下我们的每个请求都会在同一个系统中进行输出.但是在微服务架构中一个请求可能设置一到多个服务进行处理.服务之间相互依 ...
最新文章
- 人工智能的核心是“算法”,医生才是主角!
- MySQL的binary类型操作
- utc时间 单位换算_数学基础知识点总结,常用单位换算长度、时间、面积等分类...
- Linux虚拟机安装配置准备工作之--- VMware ( Bridge )
- 项目管理的十大谈判必杀技
- [原创] GSM/GPRS 以及CDMA区分以及相关模块选型
- 鸿蒙之下5怎么跳城池,鸿蒙之空间道尊
- python题目推荐_python题目
- 网站访问优化,未完待续
- Java 8 Features Tutorial – The ULTIMATE Guide
- 微服务架构之Spring Cloud Eureka入门程序
- JAVA card 应用开发(六) 个人化数据的线路安全和数据安全
- Windows下Nginx负载均衡配置和优化方案
- Atitit 导航模式 面包屑 胶囊式 标签式tab 目录 1.1. 表格导航	1 2. 面包屑导航来源于童话故事中的汉塞尔利用面包屑来记录回家的路的故事	1 2.1.1. 格林童话《Hanse
- 矩阵基础概念之行列式与秩
- 杨辉三角与二项式定理
- 八皇后问题(回溯法amp;枚举法)
- 未来教育计算机二级wps office,2019年全国计算机一级WPS Office应用考试大纲
- 常见的导数公式及证明
- 示坡线高程判断_【专题突破】等高线地形图的判读技巧