使用OpenTracing跟踪Go中的HTTP请求延迟

解道jdon 2021-05-03 08:35:46
http 使用 跟踪 请求 OpenTracing


在Go 1.7,我们有一个新包/ HTTP / httptrace提供了一个方便的机制,观察一个HTTP请求时会发生什么。在本文中,将说明如何能在分布式跟踪的情况下被使用,通过使用OpenTracing API跟踪观察一个客户端和一个服务器,并可视化的结果显示在Zipkin UI

什么是分布式跟踪和OpenTracing?

分布式跟踪是监测和分析微服务架构系统,导出结果到为X-TRACE,如谷歌的Dapper和Twitter的Zipkin 。 它们的底层原理是分布式环境传播 ,其中涉及的某些元数据与进入系统的每个请求相关联,并且跨线程和进程边界传播元数据跟随请求进出各种微服务调用。 如果我们为每个入站请求分配一个唯一的ID并将其作为分布式上下文的一部分,那么我们可以将来自多个线程和多个进程的各种性能分析数据合并到统一的表示我们系统执行请求的“跟踪”中。

分布式跟踪需要使用Hook钩子和上下文传播机制来测试应用程序代码(或其使用的框架)。 当我们在Uber开始构建我们的分布式跟踪系统时,我们很快意识到,没有良好的API为开发人员提供在编程语言之间内部一致性,那就无法绑定到指定的跟踪系统。原来,不只是我们有这种思维,2015年10月一个新的社区形成,催生了OpenTracing API,一个开放的,厂商中立的,与语言无关的分布式跟踪标准。你可以阅读更多关于Ben Sigelman有关OpenTracing动机和设计原理背后的文章 。

让我们看看代码:

import (
"net/http"
"net/http/httptrace"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/log"
"golang.org/x/net/context"
)
// We will talk about this later
var tracer opentracing.Tracer
func AskGoogle(ctx context.Context) error {
// retrieve current Span from Context
var parentCtx opentracing.SpanContext
parentSpan := opentracing.SpanFromContext(ctx);
if parentSpan != nil {
parentCtx = parentSpan.Context()
}
// start a new Span to wrap HTTP request
span := tracer.StartSpan(
"ask google",
opentracing.ChildOf(parentCtx),
)
// make sure the Span is finished once we're done
defer span.Finish()
// make the Span current in the context
ctx = opentracing.ContextWithSpan(ctx, span)
// now prepare the request
req, err := http.NewRequest("GET", "http://google.com", nil)
if err != nil {
return err
}
// attach ClientTrace to the Context, and Context to request 
trace := NewClientTrace(span)
ctx = httptrace.WithClientTrace(ctx, trace)
req = req.WithContext(ctx)
// execute the request
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
// Google home page is not too exciting, so ignore the result
res.Body.Close()
return nil
}
<p>

这里有几点要注意:

1.回避了tracer变量初始化的问题。

2.AskGoogle函数接受context.Context对象。这是为开发分布式应用程序的Go推荐方式,因为上下文对象是要让分布式环境传播。

3.我们假设上下文已经包含父跟踪Span。 OpenTracing API中的Span是用于表示由微服务执行的工作单元。HTTP调用就是可以包裹在跟踪Span中的操作案例。 当我们运行处理入站请求的服务时,服务通常会为每个请求创建一个跟踪范围,并将其存储在上下文中,以便在对另一个服务进行下游调用时可用(在我们的示例中为google.com )。

4.我们启动一个新的子Span来包装出站HTTP调用。 如果父Span缺失,这是好方法。

5.最后,在做出HTTP请求之前,我们实例化一个ClientTrace并将其附加到请求。

ClientTrace结构是httptrace的基本构建块 。它允许我们在HTTP请求的生命周期内注册将由HTTP客户端执行的回调函数。 例如,ClientTrace结构有这样的方法:

type ClientTrace struct {
...
// DNSStart is called when a DNS lookup begins.
DNSStart func(DNSStartInfo)
// DNSDone is called when a DNS lookup ends.
DNSDone func(DNSDoneInfo)
...
}
<p>

我们在NewClientTrace方法中创建这个结构的一个实例:

func NewClientTrace(span opentracing.Span) *httptrace.ClientTrace {
trace := &clientTrace{span: span}
return &httptrace.ClientTrace {
DNSStart: trace.dnsStart,
DNSDone: trace.dnsDone,
}
}
// clientTrace holds a reference to the Span and
// provides methods used as ClientTrace callbacks
type clientTrace struct {
span opentracing.Span
}
func (h *clientTrace) dnsStart(info httptrace.DNSStartInfo) {
h.span.LogKV(
log.String("event", "DNS start"),
log.Object("host", info.Host),
)
}
func (h *clientTrace) dnsDone(httptrace.DNSDoneInfo) {
h.span.LogKV(log.String("event", "DNS done"))
}
<p>

我们为DBBStart和DNSDone事件实现注册两个回调函数,通过私有结构clientTrace保有一个指向跟踪Span。 在回调方法中,我们使用Span的键值记录API来记录事件的信息,以及Span本身隐含捕获的时间戳。

你不是说关于UI的东西吗?

OpenTracing API的工作方式是,一旦调用跟踪Span的Finish()方法,Span捕获的数据将发送到跟踪系统后端,通常在后台异步发送。然后,我们可以使用跟踪系统UI来查找跟踪并在时间线上将其可视化。 然而,上述例子只是为了说明使用OpenTracing与httptrace的原理。对于真正的工作示例,我们将使用现有的库https://github.com/opentracing-contrib/go-stdlib 。 使用这个库我们的客户端代码不需要担心跟踪实际的HTTP调用。但是,我们仍然希望创建一个顶层跟踪Span来表示客户端应用程序的整体执行,并记录任何错误。

package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"github.com/opentracing-contrib/go-stdlib/nethttp"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
otlog "github.com/opentracing/opentracing-go/log"
"golang.org/x/net/context"
)
func runClient(tracer opentracing.Tracer) {
// nethttp.Transport from go-stdlib will do the tracing
c := &http.Client{Transport: &nethttp.Transport{}}
// create a top-level span to represent full work of the client
span := tracer.StartSpan(client)
span.SetTag(string(ext.Component), client)
defer span.Finish()
ctx := opentracing.ContextWithSpan(context.Background(), span)
req, err := http.NewRequest(
"GET",
fmt.Sprintf("http://localhost:%s/", *serverPort),
nil,
)
if err != nil {
onError(span, err)
return
}
req = req.WithContext(ctx)
// wrap the request in nethttp.TraceRequest
req, ht := nethttp.TraceRequest(tracer, req)
defer ht.Finish()
res, err := c.Do(req)
if err != nil {
onError(span, err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
onError(span, err)
return
}
fmt.Printf("Received result: %s\n", string(body))
}
func onError(span opentracing.Span, err error) {
// handle errors by recording them in the span
span.SetTag(string(ext.Error), true)
span.LogKV(otlog.Error(err))
log.Print(err)
}
<p>

上面的客户端代码调用本地服务器。 让我们实现它。

package main
import (
"fmt"
"io"
"log"
"net/http"
"time"
"github.com/opentracing-contrib/go-stdlib/nethttp"
"github.com/opentracing/opentracing-go"
)
func getTime(w http.ResponseWriter, r *http.Request) {
log.Print("Received getTime request")
t := time.Now()
ts := t.Format("Mon Jan _2 15:04:05 2006")
io.WriteString(w, fmt.Sprintf("The time is %s", ts))
}
func redirect(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r,
fmt.Sprintf("http://localhost:%s/gettime", *serverPort), 301)
}
func runServer(tracer opentracing.Tracer) {
http.HandleFunc("/gettime", getTime)
http.HandleFunc("/", redirect)
log.Printf("Starting server on port %s", *serverPort)
http.ListenAndServe(
fmt.Sprintf(":%s", *serverPort),
// use nethttp.Middleware to enable OpenTracing for server
nethttp.Middleware(tracer, http.DefaultServeMux))
}

注意,客户端向根端点“/”发出请求,但服务器将其重定向到“/ gettime”端点。 这样做允许我们更好地说明如何在跟踪系统中捕获跟踪。

Tracing HTTP request latency in Go with OpenTracin

[该贴被banq于2016-11-25 09:00修改过]

版权声明
本文为[解道jdon]所创,转载请带上原文链接,感谢
https://www.jdon.com/48551

  1. HTML + CSS + JavaScript to achieve cool Fireworks (cloud like particle text 3D opening)
  2. HTML + CSS + JavaScript realizes 520 advertising love tree (including music), which is necessary for programmers to express themselves
  3. Solve the problem of Web front-end deployment server (it can be deployed online without a server)
  4. HTML + CSS + JS make wedding countdown web page template (520 / Tanabata Valentine's Day / programmer advertisement)
  5. What else can driverless minibus do besides "Park connection"?
  6. Cloud native leads the era of all cloud development
  7. NRM mirror source management tool
  8. Bring it to you, flex Jiugong
  9. Lolstyle UI component development practice (II) -- button group component
  10. Deconstruction assignment in ES6
  11. Luo 2 peerless Tang clan was officially launched. The official gave a key point, and the broadcast time was implied
  12. 20初识前端HTML(1)
  13. 当新零售遇上 Serverless
  14. 20 initial knowledge of front-end HTML (1)
  15. When new retail meets serverless
  16. [golang] - go into go language lesson 5 type conversion
  17. [golang] - go into go language lesson 6 conditional expression
  18. HTML5(八)——SVG 之 path 详解
  19. HTML5 (8) -- detailed explanation of SVG path
  20. 需要开通VIP以后页面内容才能复制怎么办?控制台禁用javascript即可
  21. Web前端|CSS入门教程(超详细的CSS使用讲解,适合前端初学者)
  22. 实践积累 —— 用Vue3简单写一个单行横向滚动组件
  23. Serverless 全能选手,再下一城
  24. What if you need to open a VIP to copy the page content? Just disable JavaScript on the console
  25. Web front end | CSS introductory tutorial (super detailed CSS explanation, suitable for front-end beginners)
  26. Practice accumulation - write a single line horizontal scroll component simply with vue3
  27. Dili Reba is thin again. She looks elegant and high in a strapless hollow skirt, and her "palm waist" is beautiful to a new height
  28. Serverless all-round player, next city
  29. The difference between MySQL semi synchronous replication and lossless semi synchronous replication
  30. Vue表单设计器的终极解决方案
  31. The ultimate solution for Vue form designer
  32. Nginx从理论到实践超详细笔记
  33. Yu Shuxin's red backless swimsuit is split to the waist and tail, with a concave convex figure and excessive color matching, and his face is white to dazzling
  34. Nginx ultra detailed notes from theory to practice
  35. 【动画消消乐|CSS】086.炫酷水波浪Loading过渡动画
  36. typecho全站启用https
  37. CCTV has another popular employee. The off-site interpretation is very professional, and the appearance ability is no less than that of Wang Bingbing
  38. [animation Xiaole | CSS] 086. Cool water wave loading transition animation
  39. Enable HTTPS in Typecho
  40. 50天用JavaScript完成50个web项目,我学到了什么?
  41. 根据JavaScript中原生的XMLHttpRequest实现jQuery的Ajax
  42. What have I learned from completing 50 web projects with JavaScript in 50 days?
  43. "My neighbor doesn't grow up" has hit the whole network. There are countless horse music circles, and actor Zhou Xiaochuan has successfully made a circle
  44. 根据JavaScript中原生的XMLHttpRequest实现jQuery的Ajax
  45. Implement the Ajax of jQuery according to the native XMLHttpRequest in JavaScript
  46. Implement the Ajax of jQuery according to the native XMLHttpRequest in JavaScript
  47. 30 + women still wear less T-shirts and jeans. If they wear them like stars, they will lose weight
  48. 数栈技术分享前端篇:TS,看你哪里逃~
  49. Several stack technology sharing front end: TS, see where you escape~
  50. 舍弃Kong和Nginx,Apache APISIX 在趣链科技 BaaS 平台的落地实践
  51. Abandon the landing practice of Kong and nginx, Apache apisik on the baas platform of fun chain technology
  52. 浪迹天涯king教你用elementui做复杂的表格,去处理报表数据(合并表头,合并表体行和列)
  53. 前端HTML两万字图文大总结,快来看看你会多少!【️熬夜整理&建议收藏️】
  54. Wandering around the world king teaches you to use elementui to make complex tables and process report data (merge header, merge table body rows and columns)
  55. 路由刷新数据丢失 - vuex数据读取的问题
  56. Front end HTML 20000 word graphic summary, come and see how much you can【 Stay up late to sort out & suggestions]
  57. Route refresh data loss - vuex data reading problem
  58. Systemctl系统启动Nginx服务脚本
  59. Systemctl system startup nginx service script
  60. sleepless