Gerber 格式详解
gerber中文
gerber,gerber

文件:590m.com/f/25127180-487459253-79168e(访问密码:551685)

以下内容无关:

-------------------------------------------分割线---------------------------------------------

部署 Jaeger
这里我们需要部署一个 Jaeger 实例,以供微服务以及后面学习需要。

使用 Docker 部署很简单,只需要执行下面一条命令即可:

docker run -d -p 5775:5775/udp -p 16686:16686 -p 14250:14250 -p 14268:14268 jaegertracing/all-in-one:latest
访问 16686 端口,即可看到 UI 界面。

后面我们生成的链路追踪信息会推送到此服务,而且可以通过 Jaeger UI 查询这些追踪信息。

JaegerUI

从示例了解 Jaeger Client Go
这里,我们主要了解一些 Jaeger Client 的接口和结构体,了解一些代码的使用。

为了让读者方便了解 Trace、Span 等,可以看一下这个 Json 的大概结构:

    {"traceID": "2da97aa33839442e","spans": [{"traceID": "2da97aa33839442e","spanID": "ccb83780e27f016c","flags": 1,"operationName": "format-string","references": [...],"tags": [...],"logs": [...],"processID": "p1","warnings": null},... ...],"processes": {"p1": {"serviceName": "hello-world","tags": [...]},"p2": ...,"warnings": null}

创建一个 client1 的项目,然后引入 Jaeger client 包。

go get -u github.com/uber/jaeger-client-go/
然后引入包

import (
“github.com/uber/jaeger-client-go”
)
了解 trace、span
链路追踪中的一个进程使用一个 trace 实例标识,每个服务或函数使用一个 span 标识,jaeger 包中有个函数可以创建空的 trace:

tracer := opentracing.GlobalTracer() // 生产中不要使用
然后就是调用链中,生成父子关系的 Span:

func main() {
tracer := opentracing.GlobalTracer()
// 创建第一个 span A
parentSpan := tracer.StartSpan(“A”)
defer parentSpan.Finish() // 可手动调用 Finish()

}
func B(tracer opentracing.Tracer,parentSpan opentracing.Span){
// 继承上下文关系,创建子 span
childSpan := tracer.StartSpan(
“B”,
opentracing.ChildOf(parentSpan.Context()),
)
defer childSpan.Finish() // 可手动调用 Finish()
}
每个 span 表示调用链中的一个结点,每个结点都需要明确父 span。

现在,我们知道了,如何生成 trace{span1,span2},且 span1 -> span2 即 span1 调用 span2,或 span1 依赖于 span2。

tracer 配置
由于服务之间的调用是跨进程的,每个进程都有一些特点的标记,为了标识这些进程,我们需要在上下文间、span 携带一些信息。

例如,我们在发起请求的第一个进程中,配置 trace,配置服务名称等。

// 引入 jaegercfg “github.com/uber/jaeger-client-go/config”
cfg := jaegercfg.Configuration{
ServiceName: “client test”, // 对其发起请求的的调用链,叫什么服务
Sampler: &jaegercfg.SamplerConfig{
Type: jaeger.SamplerTypeConst,
Param: 1,
},
Reporter: &jaegercfg.ReporterConfig{
LogSpans: true,
},
}

Sampler 是客户端采样率配置,可以通过 sampler.type 和 sampler.param 属性选择采样类型,后面详细聊一下。

Reporter 可以配置如何上报,后面独立小节聊一下这个配置。

传递上下文的时候,我们可以打印一些日志:

jLogger := jaegerlog.StdLogger

配置完毕后就可以创建 tracer 对象了:

tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(jLogger),
)defer closer.Close()
if err != nil {
}

完整代码如下:

import (
“github.com/opentracing/opentracing-go”
“github.com/uber/jaeger-client-go”
jaegercfg “github.com/uber/jaeger-client-go/config”
jaegerlog “github.com/uber/jaeger-client-go/log”
)

func main() {

cfg := jaegercfg.Configuration{ServiceName: "client test", // 对其发起请求的的调用链,叫什么服务Sampler: &jaegercfg.SamplerConfig{Type:  jaeger.SamplerTypeConst,Param: 1,},Reporter: &jaegercfg.ReporterConfig{LogSpans: true,},
}jLogger := jaegerlog.StdLogger
tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(jLogger),
)defer closer.Close()
if err != nil {
}// 创建第一个 span A
parentSpan := tracer.StartSpan("A")
defer parentSpan.Finish()B(tracer,parentSpan)

}

func B(tracer opentracing.Tracer, parentSpan opentracing.Span) {
// 继承上下文关系,创建子 span
childSpan := tracer.StartSpan(
“B”,
opentracing.ChildOf(parentSpan.Context()),
)
defer childSpan.Finish()
}
启动后:

2021/03/30 11:14:38 Initializing logging reporter
2021/03/30 11:14:38 Reporting span 689df7e83255d05d:75668e8ed5ec61da:689df7e83255d05d:1
2021/03/30 11:14:38 Reporting span 689df7e83255d05d:689df7e83255d05d:0000000000000000:1
2021/03/30 11:14:38 DEBUG: closing tracer
2021/03/30 11:14:38 DEBUG: closing reporter
Sampler 配置
sampler 配置代码示例:

 Sampler: &jaegercfg.SamplerConfig{Type:  jaeger.SamplerTypeConst,Param: 1,}

这个 sampler 可以使用 jaegercfg.SamplerConfig,通过 type、param 两个字段来配置采样器。

为什么要配置采样器?因为服务中的请求千千万万,如果每个请求都要记录追踪信息并发送到 Jaeger 后端,那么面对高并发时,记录链路追踪以及推送追踪信息消耗的性能就不可忽视,会对系统带来较大的影响。当我们配置 sampler 后,jaeger 会根据当前配置的采样策略做出采样行为。

详细可以参考:https://www.jaegertracing.io/docs/1.22/sampling/

jaegercfg.SamplerConfig 结构体中的字段 Param 是设置采样率或速率,要根据 Type 而定。

下面对其关系进行说明:

Type Param 说明
“const” 0或1 采样器始终对所有 tracer 做出相同的决定;要么全部采样,要么全部不采样
“probabilistic” 0.0~1.0 采样器做出随机采样决策,Param 为采样概率
“ratelimiting” N 采样器一定的恒定速率对tracer进行采样,Param=2.0,则限制每秒采集2条
“remote” 无 采样器请咨询Jaeger代理以获取在当前服务中使用的适当采样策略。
sampler.Type=“remote”/sampler.Type=jaeger.SamplerTypeRemote 是采样器的默认值,当我们不做配置时,会从 Jaeger 后端中央配置甚至动态地控制服务中的采样策略。

Reporter 配置
看一下 ReporterConfig 的定义。

type ReporterConfig struct {
QueueSize int yaml:"queueSize"
BufferFlushInterval time.Duration
LogSpans bool yaml:"logSpans"
LocalAgentHostPort string yaml:"localAgentHostPort"
DisableAttemptReconnecting bool yaml:"disableAttemptReconnecting"
AttemptReconnectInterval time.Duration
CollectorEndpoint string yaml:"collectorEndpoint"
User string yaml:"user"
Password string yaml:"password"
HTTPHeaders map[string]string yaml:"http_headers"
}
Reporter 配置客户端如何上报追踪信息的,所有字段都是可选的。

这里我们介绍几个常用的配置字段。

QUEUESIZE,设置队列大小,存储采样的 span 信息,队列满了后一次性发送到 jaeger 后端;defaultQueueSize 默认为 100;

BufferFlushInterval 强制清空、推送队列时间,对于流量不高的程序,队列可能长时间不能满,那么设置这个时间,超时可以自动推送一次。对于高并发的情况,一般队列很快就会满的,满了后也会自动推送。默认为1秒。

LogSpans 是否把 Log 也推送,span 中可以携带一些日志信息。

LocalAgentHostPort 要推送到的 Jaeger agent,默认端口 6831,是 Jaeger 接收压缩格式的 thrift 协议的数据端口。

CollectorEndpoint 要推送到的 Jaeger Collector,用 Collector 就不用 agent 了。

例如通过 http 上传 trace:

 Reporter: &jaegercfg.ReporterConfig{LogSpans:           true,CollectorEndpoint: "http://127.0.0.1:14268/api/traces",},

据黑洞大佬的提示,HTTP 走的就是 thrift,而 gRPC 是 .NET 特供,所以 reporter 格式只有一种,而且填写 CollectorEndpoint,我们注意要填写完整的信息。

完整代码测试:

import (
“bufio”
“github.com/opentracing/opentracing-go”
“github.com/uber/jaeger-client-go”
jaegercfg “github.com/uber/jaeger-client-go/config”
jaegerlog “github.com/uber/jaeger-client-go/log”
“os”
)

func main() {

var cfg = jaegercfg.Configuration{ServiceName: "client test", // 对其发起请求的的调用链,叫什么服务Sampler: &jaegercfg.SamplerConfig{Type:  jaeger.SamplerTypeConst,Param: 1,},Reporter: &jaegercfg.ReporterConfig{LogSpans:           true,CollectorEndpoint: "http://127.0.0.1:14268/api/traces",},
}jLogger := jaegerlog.StdLogger
tracer, closer, _ := cfg.NewTracer(jaegercfg.Logger(jLogger),
)// 创建第一个 span A
parentSpan := tracer.StartSpan("A")
// 调用其它服务
B(tracer, parentSpan)
// 结束 A
parentSpan.Finish()
// 结束当前 tracer
closer.Close()reader := bufio.NewReader(os.Stdin)
_, _ = reader.ReadByte()

}
func B(tracer opentracing.Tracer, parentSpan opentracing.Span) {
// 继承上下文关系,创建子 span
childSpan := tracer.StartSpan(
“B”,
opentracing.ChildOf(parentSpan.Context()),
)
defer childSpan.Finish()
}
运行后输出结果:

2021/03/30 15:04:15 Initializing logging reporter
2021/03/30 15:04:15 Reporting span 715e0af47c7d9acb:7dc9a6b568951e4f:715e0af47c7d9acb:1
2021/03/30 15:04:15 Reporting span 715e0af47c7d9acb:715e0af47c7d9acb:0000000000000000:1
2021/03/30 15:04:15 DEBUG: closing tracer
2021/03/30 15:04:15 DEBUG: closing reporter
2021/03/30 15:04:15 DEBUG: flushed 1 spans
2021/03/30 15:04:15 DEBUG: flushed 1 spans
打开 Jaeger UI,可以看到已经推送完毕(http://127.0.0.1:16686)。

上传的trace

这时,我们可以抽象代码代码示例:

func CreateTracer(servieName string) (opentracing.Tracer, io.Closer, error) {
var cfg = jaegercfg.Configuration{
ServiceName: servieName,
Sampler: &jaegercfg.SamplerConfig{
Type: jaeger.SamplerTypeConst,
Param: 1,
},
Reporter: &jaegercfg.ReporterConfig{
LogSpans: true,
// 按实际情况替换你的 ip
CollectorEndpoint: “http://127.0.0.1:14268/api/traces”,
},
}

jLogger := jaegerlog.StdLogger
tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(jLogger),
)
return tracer, closer, err

}
这样可以复用代码,调用函数创建一个新的 tracer。这个记下来,后面要用。

分布式系统与span
前面介绍了如何配置 tracer 、推送数据到 Jaeger Collector,接下来我们聊一下 Span。请看图。

下图是一个由用户 X 请求发起的,穿过多个服务的分布式系统,A、B、C、D、E 表示不同的子系统或处理过程。

在这个图中, A 是前端,B、C 是中间层、D、E 是 C 的后端。这些子系统通过 rpc 协议连接,例如 gRPC。

一个简单实用的分布式链路追踪系统的实现,就是对服务器上每一次请求以及响应收集跟踪标识符(message identifiers)和时间戳(timestamped events)。

这里,我们只需要记住,从 A 开始,A 需要依赖多个服务才能完成任务,每个服务可能是一个进程,也可能是一个进程中的另一个函数。这个要看你代码是怎么写的。后面会详细说一下如何定义这种关系,现在大概了解一下即可。

span调用链

怎么调、怎么传
如果有了解过 Jaeger 或读过 分布式链路追踪框架的基本实现原理 ,那么已经大概了解的 Jaeger 的工作原理。

jaeger 是分布式链路追踪工具,如果不用在跨进程上,那么 Jaeger 就失去了意义。而微服务中跨进程调用,一般有 HTTP 和 gRPC 两种,下面将来讲解如何在 HTTP、gPRC 调用中传递 Jaeger 的 上下文。

HTTP,跨进程追踪
A、B 两个进程,A 通过 HTTP 调用 B 时,通过 Http Header 携带 trace 信息(称为上下文),然后 B 进程接收后,解析出来,在创建 trace 时跟传递而来的 上下文关联起来。

一般使用中间件来处理别的进程传递而来的上下文。inject 函数打包上下文到 Header 中,而 extract 函数则将其解析出来。

这里我们分为两步,第一步从 A 进程中传递上下文信息到 B 进程,为了方便演示已经实践,我们使用 client-webserver 的形式,编写代码。

客户端
在 A 进程新建一个方法:

// 请求远程服务,获得用户信息
func GetUserInfo(tracer opentracing.Tracer, parentSpan opentracing.Span) {
// 继承上下文关系,创建子 span
childSpan := tracer.StartSpan(
“B”,
opentracing.ChildOf(parentSpan.Context()),
)

url := "http://127.0.0.1:8081/Get?username=痴者工良"
req,_ := http.NewRequest("GET", url, nil)
// 设置 tag,这个 tag 我们后面讲
ext.SpanKindRPCClient.Set(childSpan)
ext.HTTPUrl.Set(childSpan, url)
ext.HTTPMethod.Set(childSpan, "GET")
tracer.Inject(childSpan.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
resp, _ := http.DefaultClient.Do(req)
_ = resp   // 丢掉
defer childSpan.Finish()

}
然后复用前面提到的 CreateTracer 函数。

main 函数改成:

func main() {
tracer, closer, _ := CreateTracer(“UserinfoService”)
// 创建第一个 span A
parentSpan := tracer.StartSpan(“A”)
// 调用其它服务
GetUserInfo(tracer, parentSpan)
// 结束 A
parentSpan.Finish()
// 结束当前 tracer
closer.Close()

reader := bufio.NewReader(os.Stdin)
_, _ = reader.ReadByte()

}
完整代码可参考:https://github.com/whuanle/DistributedTracingGo/issues/1

Web 服务端
服务端我们使用 gin 来搭建。

新建一个 go 项目,在 main.go 目录中,执行 go get -u github.com/gin-gonic/gin。

创建一个函数,该函数可以从创建一个 tracer,并且继承其它进程传递过来的上下文信息。

// 从上下文中解析并创建一个新的 trace,获得传播的 上下文(SpanContext)
func CreateTracer(serviceName string, header http.Header) (opentracing.Tracer,opentracing.SpanContext, io.Closer, error) {
var cfg = jaegercfg.Configuration{
ServiceName: serviceName,
Sampler: &jaegercfg.SamplerConfig{
Type: jaeger.SamplerTypeConst,
Param: 1,
},
Reporter: &jaegercfg.ReporterConfig{
LogSpans: true,
// 按实际情况替换你的 ip
CollectorEndpoint: “http://127.0.0.1:14268/api/traces”,
},
}

jLogger := jaegerlog.StdLogger
tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(jLogger),
)
// 继承别的进程传递过来的上下文
spanContext, _ := tracer.Extract(opentracing.HTTPHeaders,opentracing.HTTPHeadersCarrier(header))
return tracer, spanContext, closer, err

}
为了解析 HTTP 传递而来的 span 上下文,我们需要通过中间件来解析了处理一些细节。

func UseOpenTracing() gin.HandlerFunc {
handler := func(c *gin.Context) {
// 使用 opentracing.GlobalTracer() 获取全局 Tracer
tracer,spanContext, closer, _ := CreateTracer(“userInfoWebService”, c.Request.Header)
defer closer.Close()
// 生成依赖关系,并新建一个 span、
// 这里很重要,因为生成了 References []SpanReference 依赖关系
startSpan:= tracer.StartSpan(c.Request.URL.Path,ext.RPCServerOption(spanContext))
defer startSpan.Finish()

 // 记录 tag// 记录请求 Urlext.HTTPUrl.Set(startSpan, c.Request.URL.Path)// Http Methodext.HTTPMethod.Set(startSpan, c.Request.Method)// 记录组件名称ext.Component.Set(startSpan, "Gin-Http")// 在 header 中加上当前进程的上下文信息c.Request=c.Request.WithContext(opentracing.ContextWithSpan(c.Request.Context(),startSpan))// 传递给下一个中间件c.Next()// 继续设置 tagext.HTTPStatusCode.Set(startSpan, uint16(c.Writer.Status()))
}return handler

}
别忘记了 API 服务:

func GetUserInfo(ctx *gin.Context) {
userName := ctx.Param(“username”)
fmt.Println(“收到请求,用户名称为:”, userName)
ctx.String(http.StatusOK, “他的博客是 https://whuanle.cn”)
}
然后是 main 方法:

func main() {
r := gin.Default()
// 插入中间件处理
r.Use(UseOpenTracing())
r.GET("/Get",GetUserInfo)
r.Run(“0.0.0.0:8081”) // listen and serve on 0.0.0.0:8080 (for windows “localhost:8080”)
}

Gerber 格式详解相关推荐

  1. Java字节码(.class文件)格式详解(一)

    原文链接:http://www.blogjava.net/DLevin/archive/2011/09/05/358033.html 小介:去年在读<深入解析JVM>的时候写的,记得当时还 ...

  2. php serialize取值,PHP 序列化(serialize)格式详解

    PHP 序列化(serialize)格式详解(转) 1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PH ...

  3. php serialize mysql_php 序列化(serialize)格式详解

    1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PHP 手册中对这两个函数的说明仅限于如何使用,而对序列 ...

  4. 三维重建:PNG格式详解-与LibPNG使用

    PNG图像包含了骨骼信息,左边的图像比右边的大几十K,包含了骨骼信息:        PNG格式详解:https://blog.mythsman.com/post/5d2d62b4a2005d7404 ...

  5. 4-4:TCP协议之TCP头部格式详解

    文章目录 一:TCP头部格式详解 (1)4位首部长度 (2)序列号和确认应答号 A:可靠性问题 B:32位序号和确认号 (3)窗口大小 (4)标志位 (5)紧急指针 A:带外数据(out_of _ba ...

  6. PHP 序列化(serialize)格式详解

      1.前言 PHP (从 PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize.unserialize.不过在 PHP 手册中对这两个函数的说明仅限于如何使用,而对 ...

  7. 安卓camera2 API获取YUV420_888格式详解

    安卓音视频开发中的一个环节是摄像头采集数据,Android平台上摄像头采集的API有两套,camera1和camera2.本文主要讲的是camera2这套API采集数据,并指明YUV420_888格式 ...

  8. BMP格式详解<转>

    BMP格式详解 BMP文件格式详解(BMP file format) BMP文件格式,又称为Bitmap(位图)或是DIB(Device-Independent Device,设备无关位图),是Win ...

  9. 小猫爪:嵌入式小知识10-I2S,TDM,PCM等音频格式详解

    小猫爪:嵌入式小知识10-I2S,TDM,PCM等音频格式详解 1 前言 2 I2S 3 Codec模式(左/右对齐) 3.1 左对齐(MSB对齐) 3.2 右对齐(LSB对齐) 4 DSP模式 5 ...

最新文章

  1. 还剩10天,赶紧登下百度网盘,拯救你的2T存储空间吧!
  2. android登录窗口——基础编
  3. Java中通过ActionEvent判断是哪个按钮
  4. python 获取指定目录下的图片文件
  5. 百度地图JavaScript API覆盖物旋转时出现偏移
  6. 安卓应用安全指南 六、困难问题
  7. python 爬取上海体育彩票文章标题、时间、内容
  8. bootstrapValidator验证失败问题
  9. 编程大讲坛 坛坛是佳酿--编程大讲坛:C语言核心开发技术从入门到精通
  10. python 新建文件 hdfs_python使用hdfs3模块对hdfs进行操作详解
  11. 请画出一个抓虫系统的架构图并说明你的爬虫需要如何优化来提升性能
  12. 轻松自动化---selenium-webdriver(python) (十一)
  13. accept 返回0_Netty深入浅出系列:Netty源码分析之accept过程
  14. web前端技术分享文档
  15. android获得手机目录,关于android手机文件目录的收集
  16. linux安装之后缺少命令,linux下rarlinux安装后找不到rar命令处理方法
  17. 如何绕过开机密码开启计算机,win10怎么绕过开机密码,win10如何强制跳过密码
  18. 【高级渲染特性】抗锯齿技术介绍
  19. 搭建iis自己可以别人_自己也可以搭建一台好用实惠的软导一体机!
  20. win10创建新的计算机用户名和密码,win10电脑怎么新建用户?高手教你在win10新建用户的方法...

热门文章

  1. 视频号怎么吸引眼球,视频号内容如何吸引用户观看:国仁网络资讯
  2. iOS从相册选择视频和保存视频到相册
  3. 批量ping指定端口,批量测试IP地址是否通
  4. 设计模式之禅【访问者模式】
  5. 防止amazon账号关联,你真需要注意的这些点
  6. 【Mac环境】mac在线安装brew时返回443链接被拒绝或超时错误解决方案
  7. Rhombus 使用 Wolfspeed SiC 加快电动汽车充电速度
  8. Linux实战:awl-2.0工具模拟洪水攻击,建立大量的TCP连接
  9. 蓝色巨人IBM(International Business Machines)
  10. RT-thread实现USB虚拟U盘 模拟读卡器读写sd0