具体我们直接看代码。
clientA
package main
import (
"flag"
"fmt"
"github.com/opentracing-contrib/go-stdlib/nethttp"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/uber/jaeger-client-go"
jlog "github.com/uber/jaeger-client-go/log"
"golang.org/x/net/context"
"io/ioutil"
"log"
"net/http"
"time"
"tracingdemo/examples"
)
const (
HttpServerName = "httpServer-go"
HttpClientName = "httpClient-go"
)
func main() {
examples.HttpTest()
}
func NewJaegerTracerDirect(service string) opentracing.Tracer {
tracer, _ := jaeger.NewTracer(service,
jaeger.NewConstSampler(true),
jaeger.NewLoggingReporter(jlog.StdLogger),
)
return tracer
}
func HttpTest() {
for {
time.Sleep(3 * time.Second)
tracer := NewJaegerTracerDirect(HttpClientName)
runClient(tracer, "http://localhost:8070/", HttpClientName, nil)
}
}
// 这里是核心
func runClient(tracer opentracing.Tracer, httpUrl string, spanName string, r *http.Request) {
c := &http.Client{Transport: &nethttp.Transport{}}
span := tracer.StartSpan(spanName)
// 关键步骤:判断有没有上游服务,有的话就获取上有透传过来的信息,再构造一个子span
if r != nil {
spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
span = tracer.StartSpan(spanName, opentracing.ChildOf(spanCtx))
}
// 获取span上下文
ctx := opentracing.ContextWithSpan(context.Background(), span)
// 加点标签(可加可不加)
span.SetTag(string(ext.Component), spanName)
defer span.Finish()
req, err := http.NewRequest(
"GET",
httpUrl,
nil,
)
//关键步骤:透传header,不然下游拿不到traceId和spanId,无法联动
tracer.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
if err != nil {
onError(span, err)
return
}
req = req.WithContext(ctx)
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)
}
serverB
package main
import (
"flag"
"fmt"
"github.com/opentracing-contrib/go-stdlib/nethttp"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/uber/jaeger-client-go"
jlog "github.com/uber/jaeger-client-go/log"
"golang.org/x/net/context"
"io/ioutil"
"log"
"net/http"
"time"
"tracingdemo/examples"
)
const (
HttpServerName = "httpServer-go-B"
)
func main() {
startServer()
}
func NewJaegerTracerDirect(service string) opentracing.Tracer {
tracer, _ := jaeger.NewTracer(service,
jaeger.NewConstSampler(true),
jaeger.NewLoggingReporter(jlog.StdLogger),
)
return tracer
}
func startServer() {
runServer(NewJaegerTracerDirect(HttpServerName))
}
func runClient(tracer opentracing.Tracer, httpUrl string, spanName string, r *http.Request) {
c := &http.Client{Transport: &nethttp.Transport{}}
span := tracer.StartSpan(spanName)
if r != nil {
spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
span = tracer.StartSpan(spanName, opentracing.ChildOf(spanCtx))
}
span.SetTag(string(ext.Component), spanName)
defer span.Finish()
ctx := opentracing.ContextWithSpan(context.Background(), span)
req, err := http.NewRequest(
"GET",
httpUrl,
nil,
)
tracer.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
if err != nil {
onError(span, err)
return
}
req = req.WithContext(ctx)
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) {
span.SetTag(string(ext.Error), true)
span.LogKV(otlog.Error(err))
log.Print(err)
}
func runServer(tracer opentracing.Tracer) {
http.HandleFunc("/", getDemo(tracer))
err := http.ListenAndServe(
":8070",
nethttp.Middleware(tracer, http.DefaultServeMux))
if err != nil {
log.Fatalf("Cannot start server: %s", err)
}
}
func getDemo(tracer opentracing.Tracer) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.Header) // 看看有没有把traceId传下来
runClient(tracer, "http://localhost:8071/", HttpServerName, r)
}
}
serverC
package main
import (
"flag"
"fmt"
"github.com/opentracing-contrib/go-stdlib/nethttp"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/uber/jaeger-client-go"
jlog "github.com/uber/jaeger-client-go/log"
"golang.org/x/net/context"
"io/ioutil"
"log"
"net/http"
"time"
"tracingdemo/examples"
)
const (
HttpServerName = "httpServer-go-C"
)
func main() {
startServer()
}
func NewJaegerTracerDirect(service string) opentracing.Tracer {
tracer, _ := jaeger.NewTracer(service,
jaeger.NewConstSampler(true),
jaeger.NewLoggingReporter(jlog.StdLogger),
)
return tracer
}
func startServer() {
runServer(NewJaegerTracerDirect(HttpServerName))
}
func runClient(tracer opentracing.Tracer, httpUrl string, spanName string, r *http.Request) {
c := &http.Client{Transport: &nethttp.Transport{}}
span := tracer.StartSpan(spanName)
if r != nil {
spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
span = tracer.StartSpan(spanName, opentracing.ChildOf(spanCtx))
}
span.SetTag(string(ext.Component), spanName)
defer span.Finish()
ctx := opentracing.ContextWithSpan(context.Background(), span)
req, err := http.NewRequest(
"GET",
httpUrl,
nil,
)
tracer.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
if err != nil {
onError(span, err)
return
}
req = req.WithContext(ctx)
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) {
span.SetTag(string(ext.Error), true)
span.LogKV(otlog.Error(err))
log.Print(err)
}
func runServer(tracer opentracing.Tracer) {
http.HandleFunc("/", getDemo(tracer))
err := http.ListenAndServe(
":8071",
nethttp.Middleware(tracer, http.DefaultServeMux))
if err != nil {
log.Fatalf("Cannot start server: %s", err)
}
}
func getDemo(tracer opentracing.Tracer) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.Header) // 看看有没有把traceId传下来
}
}
通过以上代码,您就可以实现A->B->C的调用链路了。
总结起来要把链路串起来,最关键是client要透传traceId和判断是否是跟节点。