golang zipkin http_Zipkin 快速上手 —— 五步搞定基础分布式追踪 (概念+代码)【翻译自外文博客】...

1014abcfbd42e476da2f5caf9f7975ec.png

Zipkin 是基于 Java 的分布式追踪系统,用于收集和查找来自分布式系统的数据。伴随着 HTTP 请求的发出,有太多事情可能发生了:它们包括对数据库引擎,缓存服务器或任何其他微服务组件(如另一个微服务)的调用。每当此时, Zipkin 就能派上用场了。针对这种微服务拓扑结构非常复杂的情况,您在进行故障排除时就可以使用 Zipkin 进行可视化等诸多功能。

在本文中,您将了解 Zipkin 是什么,以及它在微服务或分布式系统中的重要性。按照《 OpenCensus指南》中使用的示例应用程序,您将学习如何在Zipkin中发送和查看跟踪。

这篇文章是由 Christian Meléndez 撰写的。 Christian 是一名技术专家,最初是一名软件开发人员,之后成为一名云架构师。
原文链接: https://www. scalyr.com/blog/zipkin- tutorial-distributed-tracing/
翻译By : 田同学 || Contact me :leontian1024@gmail.com|| https:// github.com/XinyaoTian
注明原出处及译者信息,欢迎转载。

什么是 Zipkin ?

Zipkin 是一个根据 Google 发表的论文“ Dapper” 进行开源实现的分布式跟踪系统。 Dapper是Google 公司内部的分布式追踪系统,用于生产环境中的系统分布式跟踪。 Google在其论文中对此进行了解释,他们“构建了Dapper,以向Google开发人员提供有关复杂分布式系统行为的更多信息。”从不同角度观察系统对于故障排除至关重要,在系统复杂且分布式的情况下更是如此。

Zipkin可帮助您准确确定对应用程序的请求在哪里花费了更多时间。无论是代码内部的调用,还是对另一服务的内部或外部API调用,您都可以对系统进行检测以共享上下文。微服务通常通过将请求与唯一ID相关联来共享上下文。此外,在系统太复杂的情况下,您可以选择仅使用样本追踪 (sample trace ,一种占用资源比例更低的追踪方式) 来减少系统开销。

举个例子,如果您的微服务应用的响应时间过长,使用Zipkin的话,您就可以轻松了解应用程序是否花费了大部分时间来查询数据库。通过 Zipkin 提供的请求延迟时间可视化,您可能会发现导致请求时间过长的原因,是由于高速缓存服务器已关闭,故所有调用都直接发送到数据库,从而增加了微服务的延迟

现在,让我们看一下Zipkin的组件,以进一步了解追踪数据如何到达Zipkin。

Zipkin体系结构组件

Zipkin不仅仅是一种以可视化方式查看系统跟踪的工具。了解更多有关Zipkin组件的信息可能有助于了解为什么Zipkin不会降低整体应用程序的性能。首先,您需要了解API的请求发送到Zipkin时会发生什么。使用 Zipkin 的前提是需要先向代码添加 Instrumentation(找不到合适的翻译词汇,这里就用英文原词了。译者注)。 Zipkin 具有用于绝大多数编程语言添加 Instrumentation 的官方和社区库。您只需要学习如何将其添加到代码中即可。下面,就是见证奇迹的时刻。

一旦请求结束(在用户收到响应之前),Zipkin就会以单个 span (Zipkin数据追踪的最小计量单位) 的形式将跟踪数据异步发送到 Zipkin 的数据追踪收集器。通常,跟踪是通过带有 context header 的 HTTP 协议发送的。如果代码调用了使用 OpenCensus 和 Zipkin 的其他服务,则该服务将接收这个 HTTP header。 Zipkin 就是这样进行追踪的。收集器就是一个 daemon,用于验证和索引存储中的数据。在 Zipkin 中,持久化组件是可选配的组件,您可以选用 Cassandra,Elasticsearch 或 MySQL。在追踪数据被存储后,Zipkin 的查询服务将提供一个JSON API,任何人都可以使用它来获取追踪数据。Zipkin 自带有一个可访问的用户界面(Web UI),任何人在进行进行故障排除时都可以使用它。

您现在可能会害怕 Zipkin 进行系统追踪会占用很多计算资源。但其实说真的,相比于您的业务应用,它只会占用非常小的一丁点资源。我在这里建议您再每次大范围更新您的业务应用之前,都先在 Zipkin 的监测下跑一遍性能测试,以便心里有 balance(笑)。

现在,让我们看看如何使用OpenCensus和Zipkin将分布式跟踪集成到应用程序中。

使用OpenCensus和Zipkin进行分布式跟踪

我将继续使用OpenCensus帖子中使用的简单的Go HTTP API。您可以在Github的https://github.com/christianhxc/opencensus-golang-tutorial 中找到代码的最新版本。我将解释将跟踪发送到Zipkin所必须添加的代码的每个部分。

1.先决条件:安装和配置

为了使本指南更易于上手,我决定使用以下工具:

使用 Docker,它将帮助您更轻松地运行其他工具。

使用 Go tools 来构建和运行示例应用程序。

在我们安装Go之后,请在您的本地环境中获取 OpenCensus 库。运行以下命令:

go get -u -v go.opencensus.io/...

现在,您可以导入 Zipkin 的库并将其添加到应用程序代码中。

2.使用OpenCensus编写Go HTTP API

我将在本节末尾提供代码的整体版本(所有这些代码段的组合),以防在使用过程中造成混淆。或者,您也可以在本地克隆Github存储库并运行该应用程序。

在main.go文件中,将以下代码添加到import部分:

"go.opencensus.io/exporter/zipkin"
"go.opencensus.io/trace"
openzipkin "github.com/openzipkin/zipkin-go"
zipkinHTTP "github.com/openzipkin/zipkin-go/reporter/http"

上面的代码包括用于Zipkin和跟踪的OpenCensus库,以及将跟踪发送到Zipkin的官方库。

为了提高代码的易读性,我们要为作报告组件 (exporter) 的 Zipkin 创建一个独立函数:

func registerZipkin(){
	localEndpoint, err := openzipkin.NewEndpoint("golangsvc", "192.168.1.61:8080") 
 	if err != nil { 
		log.Fatalf("Failed to create Zipkin exporter: %v", err)
 	} 
 	reporter := zipkinHTTP.NewReporter("http://localhost:9411/api/v2/spans") 
 	exporter := zipkin.NewExporter(reporter, localEndpoint) 
	trace.RegisterExporter(exporter) 
	trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
}

在 main() 中调用这个函数。如您所见,该函数不需要任何参数。当您运行应用程序时,每当访问 url 的“ /list” 路径时,exporter 都会向 Zipkin 发送一条跟踪记录。除此之外,您无需在代码中添加任何其他内容。

如果您想要使用样本追踪(sample trace),则删除代码中的“ trace.ApplyConfig(trace.Config {DefaultSampler:trace.AlwaysSample()})”。

如果您想要了解每个请求的详细信息,该怎么办呢?如果是对外部 API 的调用,则需要确保其他服务将跟踪数据发送到 Zipkin 。如果不是,只是出于娱乐目的,我添加了一些其他虚拟函数来模拟对另一个微服务组件(例如缓存,数据库或另一个微服务)的调用。

例如,仔细查看缓存功能:

func cache(r *http.Request) {
	_, span := trace.StartSpan(r.Context(), "cache")
	defer span.End()
	time.Sleep(time.Duration(rand.Intn(100)+1) * time.Millisecond)
}

您会看到我添加了以下代码来报告 span:“ _, span := trace.StartSpan(r.Context(), “cache”)”。如果已有Zipkin header,则该 span 将被视为同一 context 的一部分。当您在 Zipkin 的用户界面中看到完整的代码和轨迹时,就会理解我在说什么了。

# “ main.go”的完整代码将如下所示:
 
package main
 
import (
	"log"
	"math/rand"
	"net/http"
	"strings"
	"time"
 
	"go.opencensus.io/exporter/prometheus"
	"go.opencensus.io/exporter/zipkin"
	"go.opencensus.io/plugin/ochttp"
	"go.opencensus.io/stats/view"
	"go.opencensus.io/trace"
 
	openzipkin "github.com/openzipkin/zipkin-go"
	zipkinHTTP "github.com/openzipkin/zipkin-go/reporter/http"
)
 
func registerPrometheus() *prometheus.Exporter {
	pe, err := prometheus.NewExporter(prometheus.Options{
		Namespace: "golangsvc",
	})
	if err != nil {
		log.Fatalf("Failed to create Prometheus exporter: %v", err)
	}
	view.RegisterExporter(pe)
	return pe
}
 
func registerZipkin(){
	localEndpoint, err := openzipkin.NewEndpoint("golangsvc", "192.168.1.61:8080") 
 	if err != nil { 
		log.Fatalf("Failed to create Zipkin exporter: %v", err)
 	} 
 	reporter := zipkinHTTP.NewReporter("http://localhost:9411/api/v2/spans") 
 	exporter := zipkin.NewExporter(reporter, localEndpoint) 
	trace.RegisterExporter(exporter) 
	trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
}
 
func main() {
	pe := registerPrometheus()
	registerZipkin()
 
	mux := http.NewServeMux()
	mux.HandleFunc("/list", list)
 
	mux.Handle("/metrics", pe)
 
	h := &ochttp.Handler{Handler: mux}
	if err := view.Register(ochttp.DefaultServerViews...); err != nil {
		log.Fatal("Failed to register ochttp.DefaultServerViews")
	}
 
	log.Printf("Server listening! ...")
	log.Fatal(http.ListenAndServe(":8080", h))
}
 
func list(w http.ResponseWriter, r *http.Request) {
	log.Printf("Serving request: %s", r.URL.Path)
	database(r)
	serviceb(r)
 
	res := strings.Repeat("o", rand.Intn(99971)+1)
	time.Sleep(time.Duration(rand.Intn(977)+1) * time.Millisecond)
	w.Write([]byte("Hello, w" + res + "rld!"))
}
 
func database(r *http.Request) {
	cache(r)
	_, span := trace.StartSpan(r.Context(), "database")
	defer span.End()
	time.Sleep(time.Duration(rand.Intn(977)+300) * time.Millisecond)	
}
 
func cache(r *http.Request) {
	_, span := trace.StartSpan(r.Context(), "cache")
	defer span.End()
	time.Sleep(time.Duration(rand.Intn(100)+1) * time.Millisecond)
}
 
func serviceb(r *http.Request) {
	_, span := trace.StartSpan(r.Context(), "serviceb")
	defer span.End()
	time.Sleep(time.Duration(rand.Intn(800)+200) * time.Millisecond)
	servicec(r)
}
 
func servicec(r *http.Request) {
	_, span := trace.StartSpan(r.Context(), "servicec")
	defer span.End()
	time.Sleep(time.Duration(rand.Intn(700)+100) * time.Millisecond)
}

3.在本地安装Zipkin

让我们使用Docker在本地运行Zipkin。为此,请运行以下命令(请确保您的主机装有 Docker):

docker run -d -p 9411:9411 openzipkin/zipkin

现在,打开您的浏览器访问 http://localhost:9411/

4.从HTTP API生成跟踪

准备好代码并运行Zipkin后,请使用以下命令运行应用程序:

go run main.go

在浏览器中,打开URL http:// localhost:8080/list,并在每次单击“ /list”端点时刷新页面以生成跟踪。

5.查看Zipkin中的监测情况

返回Zipkin浏览器窗口,您应该看到类似以下内容:

6dd7b4c25752d1d62bb3abb16ac61ea2.png

您可以像我一样使用过滤器仅查看路径“ /list”的情况。然后,单击任何追踪轨迹(trace),您将看到类似以下的内容:

d15ae37b541d4530c750e414325d4501.png

另一个可行的选择是,您可以在应用程序的响应有效负载中包含跟踪ID,以便请确定位错误位置,以最快的速度解决客户们的投诉。

您可以精确地看到每个请求在每个功能上具体花费了多少时间。例如,您可以发现问题出在对某个微服务的调用上,并首先关注减少该微服务组件的延迟。或者,您可以使用这些跟踪来了解请求的工作流程是什么。如果多次调用依赖项(例如缓存)怎么办?使用Zipkin,可以轻松发现此类问题。

使用 Zipkin 进行故障排除

不要浪费宝贵的时间在生产环境中调试你的应用程序,要用 Zipkin!您可以添加 Instrumentation 来检测代码,从而轻松发现几乎所有问题,或者至少能了解问题可能出在哪里。我曾经使用这种方法,将 Instrumentation 插入到代码并获取每个请求的跟踪,最终发现微服务组件之间的调用产生了死循环,从而增加了服务的延迟。

Zipkin UI 可以帮助开发者,甚至是那些非专业人士提供很好的发现问题的手段,并为解决问题做出贡献。拥有这种简单又详细的可视化功能将会对故障排除很有帮助。使用 Zipkin ,在对分布式系统进行故障排除时,您将不会再像大海捞针一样陷入迷茫。

感谢您的阅读,希望对您的工作有所帮助,谢谢。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值