How to implement distributed opentracing using Go?

Shreya Singh
4 min readJun 26, 2018

This is an implementation of distributed open-tracing using Golang and Jaeger UI.

In micro services architectures of modern application development, distributed tracing instrumentation is used by developers to trace requests across the different endpoints of a system. With the higher-level distribution of services that takes place in a cloud-based environment, tracing will become a key part of the cloud infrastructure supporting those services.

In OpenTracing, a trace tells the story of a transaction or workflow as it propagates through a distributed system. The concept of the trace borrows a tool from the scientific community called a directed acyclic graph (DAG), which stages the parts of a process from a clear start to a clear end.

The github repository of the codes is available at this link:

https://github.com/singh-shreya6/opentracing.go.git

Here I have provided the code for opentracing in a distributed environment where a user is making a request on a browser at some port.The request to server 1 prints out Hello ! time is . After completing this operation, server 1 makes a request to server 2 on another port,by sending the name in request, the second server takes the name in its response body and displays Hello !

So overall the following operations takes place:

  1. A browser makes a request to http://localhost:8082/name
  2. The browser displays Hello name! time is Thu Jun 7 17:18:54 2018
  3. Server 1 sends a POST request to Sever 2 with the name at http://localhost:8081
  4. Server 2 captures this request in response body and displays Hello name!

Code for Server-1:

package main

import (
“fmt”
“net/http”
“time”
“io/ioutil”
“strings”
“context”
“go-opentracing”
“log”
)

func main() {
go_opentracing.Init(“server-1”)
s:=&http.Server{
Addr: “:8082”,
Handler: go_opentracing.HttpMiddleware(“server-1”, http.HandlerFunc(handle)),
}
log.Fatal(s.ListenAndServe())

}

func handle(w http.ResponseWriter, r *http.Request) {
t:=time.Now()
name:=r.URL.Path[1:]
time.Sleep(250 * time.Millisecond)
makeRequest(r.Context(), “http://localhost:8081",name)
fmt.Fprintf(w, “Hello %s! time is %s”, name,t.Format(“Mon Jan _2 15:04:05 2006”))
}

func makeRequest(ctx context.Context, serverAddr string, name string){
span, ctx:=go_opentracing.Introduce_span(ctx,”server-1_to_server-2")
defer span.Finish()

req, err := http.NewRequest(http.MethodPost, “http://localhost:8081", strings.NewReader(name))
if err != nil {
fmt.Printf(“http.NewRequest() error: %v\n”, err)
return
}

go_opentracing.Serialise(ctx,req)

c := &http.Client{}
resp, err := c.Do(req)
if err != nil {
fmt.Printf(“http.Do() error: %v\n”, err)
return
}
defer resp.Body.Close()

data, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf(“ioutil.ReadAll() error: %v\n”, err)
return
}
fmt.Printf(“\n%s”, string(data))
}

Code for Server-2:

package main

import (
“net/http”
“io/ioutil”
“fmt”
“log”
“go-opentracing”
“time”
)

func handle(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
serverSpan,r:= go_opentracing.Deserialize(r,”server-2")
defer serverSpan.Finish()

reqBody, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Printf(“ioutil.ReadAll() error: %v\n”, err)
w.WriteHeader(http.StatusBadRequest)
return
}

name := string(reqBody)
time.Sleep(500 * time.Millisecond)
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, “Hello %s!”, name)
}

func main() {
go_opentracing.Init(“server-2”)
s := &http.Server{
Addr: “:8081”,
Handler: go_opentracing.HttpMiddleware(“server-2”, http.HandlerFunc(handle)),
}
log.Fatal(s.ListenAndServe())
}

Go Opentracing code:

package go_opentracing

import (
“github.com/opentracing/opentracing-go”
“github.com/uber/jaeger-client-go”
“net/http”
“context”
“github.com/opentracing/opentracing-go/ext”
“log”
“time”
)

func Init(s string) {
// init open tracing
udpTransport, _ := jaeger.NewUDPTransport(“localhost:5775”, 0)
reporter:= jaeger.NewRemoteReporter(udpTransport)
sampler := jaeger.NewConstSampler(true)
tracer, _ := jaeger.NewTracer(s, sampler, reporter)
opentracing.SetGlobalTracer(tracer)
}

func Introduce_span(ctx context.Context,spanName string)(opentracing.Span,context.Context) {
span,ctx:=opentracing.StartSpanFromContext(ctx, spanName)
return span,ctx
}

func Serialise(ctx context.Context,req *http.Request){
req = req.WithContext(ctx)
if span := opentracing.SpanFromContext(ctx); span != nil {
opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header))
}
}

func Deserialize(r *http.Request,spanName string) (opentracing.Span,*http.Request){
time.Sleep(250 * time.Millisecond)
var serverSpan opentracing.Span
appSpecificOperationName := spanName
wireContext, err := opentracing.GlobalTracer().Extract(
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(r.Header))
if err != nil {
log.Fatal(“Error”)
}
serverSpan = opentracing.StartSpan(
appSpecificOperationName,
ext.RPCServerOption(wireContext))
ctx := opentracing.ContextWithSpan(context.Background(), serverSpan)
r=r.WithContext(ctx)
return serverSpan,r
}

func HttpMiddleware(serverName string, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
span, newCtx := opentracing.StartSpanFromContext(r.Context(), serverName)
defer func() {
span.Finish()
}()
r = r.WithContext(newCtx)
h.ServeHTTP(w, r)
})
}

Thus in all we get 3 spans, 1st span is of server-1, 2nd span is from server-1 to server-2 and 3rd span of server 2. The opentracing package used is Jaeger.

Spans

Jaeger is run via Docker using the following command:

docker run -d -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p5775:5775/udp -p6831:6831/udp -p6832:6832/udp -p5778:5778 -p16686:16686 -p14268:14268 -p9411:9411 jaegertracing/all-in-one:latest

or it can also be run using:

docker run -d -p 5775:5775/udp -p 16686:16686 jaegertracing/all-in-one:latest

Jaeger-UI

In Linux/Ubuntu, do remember to prefix sudo with these commands. Now the Jaeger UI runs at http://localhost:16686 All the codes in Golang.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Shreya Singh
Shreya Singh

Written by Shreya Singh

Software Development Engineer at Amazon, Google APAC Women Techmaker Scholar, C.S.E. Graduate @ NIT Patna

Responses (1)

Write a response