Commit fae2a753 authored by Jason Smith's avatar Jason Smith Committed by GitHub
Browse files

Merge pull request #30 from microservices-demo/enhancement/opentracing

Enhancement/opentracing
parents 1d394b6f 6bc65ab9
......@@ -85,3 +85,24 @@ curl http://localhost:8080/register
```bash
make dockertravisbuild
```
## Test Zipkin
To test with Zipkin
```
make
docker-compose -f docker-compose-zipkin.yml build
docker-compose -f docker-compose-zipkin.yml up
```
It takes about 10 seconds to seed data
you should see it at:
[http://localhost:9411/](http://localhost:9411)
be sure to hit the "Find Traces" button. You may need to reload the page.
when done you can run:
```
docker-compose -f docker-compose-zipkin.yml down
```
......@@ -6,8 +6,10 @@ package api
import (
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/tracing/opentracing"
"github.com/microservices-demo/user/db"
"github.com/microservices-demo/user/users"
stdopentracing "github.com/opentracing/opentracing-go"
"golang.org/x/net/context"
)
......@@ -27,24 +29,28 @@ type Endpoints struct {
// MakeEndpoints returns an Endpoints structure, where each endpoint is
// backed by the given service.
func MakeEndpoints(s Service) Endpoints {
func MakeEndpoints(s Service, tracer stdopentracing.Tracer) Endpoints {
return Endpoints{
LoginEndpoint: MakeLoginEndpoint(s),
RegisterEndpoint: MakeRegisterEndpoint(s),
HealthEndpoint: MakeHealthEndpoint(s),
UserGetEndpoint: MakeUserGetEndpoint(s),
UserPostEndpoint: MakeUserPostEndpoint(s),
AddressGetEndpoint: MakeAddressGetEndpoint(s),
AddressPostEndpoint: MakeAddressPostEndpoint(s),
CardGetEndpoint: MakeCardGetEndpoint(s),
DeleteEndpoint: MakeDeleteEndpoint(s),
CardPostEndpoint: MakeCardPostEndpoint(s),
LoginEndpoint: opentracing.TraceServer(tracer, "GET /login")(MakeLoginEndpoint(s)),
RegisterEndpoint: opentracing.TraceServer(tracer, "POST /register")(MakeRegisterEndpoint(s)),
HealthEndpoint: opentracing.TraceServer(tracer, "GET /health")(MakeHealthEndpoint(s)),
UserGetEndpoint: opentracing.TraceServer(tracer, "GET /customers")(MakeUserGetEndpoint(s)),
UserPostEndpoint: opentracing.TraceServer(tracer, "POST /customers")(MakeUserPostEndpoint(s)),
AddressGetEndpoint: opentracing.TraceServer(tracer, "GET /addresses")(MakeAddressGetEndpoint(s)),
AddressPostEndpoint: opentracing.TraceServer(tracer, "POST /addresses")(MakeAddressPostEndpoint(s)),
CardGetEndpoint: opentracing.TraceServer(tracer, "GET /cards")(MakeCardGetEndpoint(s)),
DeleteEndpoint: opentracing.TraceServer(tracer, "DELETE /")(MakeDeleteEndpoint(s)),
CardPostEndpoint: opentracing.TraceServer(tracer, "POST /cards")(MakeCardPostEndpoint(s)),
}
}
// MakeLoginEndpoint returns an endpoint via the given service.
func MakeLoginEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
var span stdopentracing.Span
span, ctx = stdopentracing.StartSpanFromContext(ctx, "login user")
span.SetTag("service", "user")
defer span.Finish()
req := request.(loginRequest)
u, err := s.Login(req.Username, req.Password)
return userResponse{User: u}, err
......@@ -54,6 +60,10 @@ func MakeLoginEndpoint(s Service) endpoint.Endpoint {
// MakeRegisterEndpoint returns an endpoint via the given service.
func MakeRegisterEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
var span stdopentracing.Span
span, ctx = stdopentracing.StartSpanFromContext(ctx, "register user")
span.SetTag("service", "user")
defer span.Finish()
req := request.(registerRequest)
id, err := s.Register(req.Username, req.Password, req.Email, req.FirstName, req.LastName)
return postResponse{ID: id}, err
......@@ -63,8 +73,16 @@ func MakeRegisterEndpoint(s Service) endpoint.Endpoint {
// MakeUserGetEndpoint returns an endpoint via the given service.
func MakeUserGetEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
var span stdopentracing.Span
span, ctx = stdopentracing.StartSpanFromContext(ctx, "get users")
span.SetTag("service", "user")
defer span.Finish()
req := request.(GetRequest)
userspan := stdopentracing.StartSpan("users from db", stdopentracing.ChildOf(span.Context()))
usrs, err := s.GetUsers(req.ID)
userspan.Finish()
if req.ID == "" {
return EmbedStruct{usersResponse{Users: usrs}}, err
}
......@@ -78,7 +96,9 @@ func MakeUserGetEndpoint(s Service) endpoint.Endpoint {
return users.User{}, err
}
user := usrs[0]
attrspan := stdopentracing.StartSpan("attributes from db", stdopentracing.ChildOf(span.Context()))
db.GetUserAttributes(&user)
attrspan.Finish()
if req.Attr == "addresses" {
return EmbedStruct{addressesResponse{Addresses: user.Addresses}}, err
}
......@@ -92,6 +112,10 @@ func MakeUserGetEndpoint(s Service) endpoint.Endpoint {
// MakeUserPostEndpoint returns an endpoint via the given service.
func MakeUserPostEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
var span stdopentracing.Span
span, ctx = stdopentracing.StartSpanFromContext(ctx, "post user")
span.SetTag("service", "user")
defer span.Finish()
req := request.(users.User)
id, err := s.PostUser(req)
return postResponse{ID: id}, err
......@@ -101,8 +125,14 @@ func MakeUserPostEndpoint(s Service) endpoint.Endpoint {
// MakeAddressGetEndpoint returns an endpoint via the given service.
func MakeAddressGetEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
var span stdopentracing.Span
span, ctx = stdopentracing.StartSpanFromContext(ctx, "get users")
span.SetTag("service", "user")
defer span.Finish()
req := request.(GetRequest)
addrspan := stdopentracing.StartSpan("addresses from db", stdopentracing.ChildOf(span.Context()))
adds, err := s.GetAddresses(req.ID)
addrspan.Finish()
if req.ID == "" {
return EmbedStruct{addressesResponse{Addresses: adds}}, err
}
......@@ -116,6 +146,10 @@ func MakeAddressGetEndpoint(s Service) endpoint.Endpoint {
// MakeAddressPostEndpoint returns an endpoint via the given service.
func MakeAddressPostEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
var span stdopentracing.Span
span, ctx = stdopentracing.StartSpanFromContext(ctx, "post address")
span.SetTag("service", "user")
defer span.Finish()
req := request.(addressPostRequest)
id, err := s.PostAddress(req.Address, req.UserID)
return postResponse{ID: id}, err
......@@ -125,8 +159,14 @@ func MakeAddressPostEndpoint(s Service) endpoint.Endpoint {
// MakeUserGetEndpoint returns an endpoint via the given service.
func MakeCardGetEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
var span stdopentracing.Span
span, ctx = stdopentracing.StartSpanFromContext(ctx, "get cards")
span.SetTag("service", "user")
defer span.Finish()
req := request.(GetRequest)
cardspan := stdopentracing.StartSpan("addresses from db", stdopentracing.ChildOf(span.Context()))
cards, err := s.GetCards(req.ID)
cardspan.Finish()
if req.ID == "" {
return EmbedStruct{cardsResponse{Cards: cards}}, err
}
......@@ -140,6 +180,10 @@ func MakeCardGetEndpoint(s Service) endpoint.Endpoint {
// MakeCardPostEndpoint returns an endpoint via the given service.
func MakeCardPostEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
var span stdopentracing.Span
span, ctx = stdopentracing.StartSpanFromContext(ctx, "post card")
span.SetTag("service", "user")
defer span.Finish()
req := request.(cardPostRequest)
id, err := s.PostCard(req.Card, req.UserID)
return postResponse{ID: id}, err
......@@ -149,6 +193,10 @@ func MakeCardPostEndpoint(s Service) endpoint.Endpoint {
// MakeLoginEndpoint returns an endpoint via the given service.
func MakeDeleteEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
var span stdopentracing.Span
span, ctx = stdopentracing.StartSpanFromContext(ctx, "delete entity")
span.SetTag("service", "user")
defer span.Finish()
req := request.(deleteRequest)
err = s.Delete(req.Entity, req.ID)
if err == nil {
......@@ -161,6 +209,10 @@ func MakeDeleteEndpoint(s Service) endpoint.Endpoint {
// MakeHealthEndpoint returns current health of the given service.
func MakeHealthEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
var span stdopentracing.Span
span, ctx = stdopentracing.StartSpanFromContext(ctx, "health check")
span.SetTag("service", "user")
defer span.Finish()
health := s.Health()
return healthResponse{Health: health}, nil
}
......
......@@ -10,9 +10,11 @@ import (
"strings"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/tracing/opentracing"
httptransport "github.com/go-kit/kit/transport/http"
"github.com/gorilla/mux"
"github.com/microservices-demo/user/users"
stdopentracing "github.com/opentracing/opentracing-go"
"github.com/prometheus/client_golang/prometheus/promhttp"
"golang.org/x/net/context"
)
......@@ -22,7 +24,7 @@ var (
)
// MakeHTTPHandler mounts the endpoints into a REST-y HTTP handler.
func MakeHTTPHandler(ctx context.Context, e Endpoints, logger log.Logger) http.Handler {
func MakeHTTPHandler(ctx context.Context, e Endpoints, logger log.Logger, tracer stdopentracing.Tracer) http.Handler {
r := mux.NewRouter().StrictSlash(false)
options := []httptransport.ServerOption{
httptransport.ServerErrorLogger(logger),
......@@ -38,70 +40,70 @@ func MakeHTTPHandler(ctx context.Context, e Endpoints, logger log.Logger) http.H
e.LoginEndpoint,
decodeLoginRequest,
encodeResponse,
options...,
append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "GET /login", logger)))...,
))
r.Methods("POST").Path("/register").Handler(httptransport.NewServer(
ctx,
e.RegisterEndpoint,
decodeRegisterRequest,
encodeResponse,
options...,
append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "POST /register", logger)))...,
))
r.Methods("GET").PathPrefix("/customers").Handler(httptransport.NewServer(
ctx,
e.UserGetEndpoint,
decodeGetRequest,
encodeResponse,
options...,
append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "GET /customers", logger)))...,
))
r.Methods("GET").PathPrefix("/cards").Handler(httptransport.NewServer(
ctx,
e.CardGetEndpoint,
decodeGetRequest,
encodeResponse,
options...,
append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "GET /cards", logger)))...,
))
r.Methods("GET").PathPrefix("/addresses").Handler(httptransport.NewServer(
ctx,
e.AddressGetEndpoint,
decodeGetRequest,
encodeResponse,
options...,
append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "GET /addresses", logger)))...,
))
r.Methods("POST").Path("/customers").Handler(httptransport.NewServer(
ctx,
e.UserPostEndpoint,
decodeUserRequest,
encodeResponse,
options...,
append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "POST /customers", logger)))...,
))
r.Methods("POST").Path("/addresses").Handler(httptransport.NewServer(
ctx,
e.AddressPostEndpoint,
decodeAddressRequest,
encodeResponse,
options...,
append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "POST /addresses", logger)))...,
))
r.Methods("POST").Path("/cards").Handler(httptransport.NewServer(
ctx,
e.CardPostEndpoint,
decodeCardRequest,
encodeResponse,
options...,
append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "POST /cards", logger)))...,
))
r.Methods("DELETE").PathPrefix("/").Handler(httptransport.NewServer(
ctx,
e.DeleteEndpoint,
decodeDeleteRequest,
encodeResponse,
options...,
append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "DELETE /", logger)))...,
))
r.Methods("GET").PathPrefix("/health").Handler(httptransport.NewServer(
ctx,
e.HealthEndpoint,
decodeHealthRequest,
encodeHealthResponse,
options...,
append(options, httptransport.ServerBefore(opentracing.FromHTTPRequest(tracer, "GET /health", logger)))...,
))
r.Handle("/metrics", promhttp.Handler())
return r
......
version: '2'
services:
user:
build:
context: .
image: weaveworksdemos/user
hostname: user
restart: always
read_only: true
environment:
- MONGO_HOST=user-db:27017
- ZIPKIN=http://zipkin:9411/api/v1/spans
- reschedule=on-node-failure
ports:
- "8084:8084"
user-db:
build:
context: ./docker/user-db/
image: weaveworksdemos/user-db
hostname: user-db
restart: always
cap_drop:
- all
cap_add:
- CHOWN
- SETGID
- SETUID
read_only: true
tmpfs:
- /tmp:rw,noexec,nosuid
environment:
- reschedule=on-node-failure
ports:
- "27017:27017"
zipkin:
image: openzipkin/zipkin
hostname: zipkin
restart: always
cap_drop:
- all
cap_add:
- CHOWN
- SETGID
- SETUID
read_only: true
tmpfs:
- /tmp:rw,noexec,nosuid
environment:
- reschedule=on-node-failure
ports:
- "9411:9411"
zipkinseed:
image: alpine
command: /bin/sh -c 'sleep 10 ; wget http://user:8084/health ; wget http://user:8084/customers ; wget http://user:8084/customers/57a98d98e4b00679b4a830af ; wget http://user:8084/cards'
hash: c5965c301f7206beca5d0b4a1a9954295a6151e365e3d3323ec081d7a591cd8e
updated: 2016-09-22T22:39:17.110450335+01:00
hash: 4ee4713d6c89f9f5c8bb53a9d7eda94c14d69ebc70a0f30f48801c416e002740
updated: 2016-12-22T08:28:36.69998899+01:00
imports:
- name: github.com/apache/thrift
version: 0c27352179e0463bde1f68757f2d77e3c222f530
subpackages:
- lib/go/thrift
- name: github.com/beorn7/perks
version: 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
subpackages:
- quantile
- name: github.com/davecgh/go-spew
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
subpackages:
- spew
- name: github.com/eapache/go-resiliency
version: b86b1ec0dd4209a588dc1285cdd471e73525c0b3
subpackages:
- breaker
- name: github.com/eapache/go-xerial-snappy
version: bb955e01b9346ac19dc29eb16586c90ded99a98c
- name: github.com/eapache/queue
version: 44cc805cf13205b55f69e14bcb69867d1ae92f98
- name: github.com/go-kit/kit
version: 988c05d06d8ee3a9c13782f0e49b2c6e4726388d
subpackages:
- endpoint
- log
- metrics
- metrics/internal/lv
- metrics/prometheus
- tracing/opentracing
- transport/http
- name: github.com/go-logfmt/logfmt
version: d4327190ff838312623b09bfeb50d7c93c8d9c1d
- name: github.com/go-stack/stack
version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82
- name: github.com/gogo/protobuf
version: 06ec6c31ff1bac6ed4e205a547a3d72934813ef3
subpackages:
- proto
- name: github.com/golang/protobuf
version: 1f49d83d9aa00e6ce4fc8258c71cc7786aec968a
subpackages:
- proto
- name: github.com/golang/snappy
version: d9eb7a3d35ec988b8585d4a0068e462c27d28380
- name: github.com/gorilla/context
version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42
- name: github.com/gorilla/mux
version: cf79e51a62d8219d52060dfc1b4e810414ba2d15
- name: github.com/klauspost/crc32
version: cb6bfca970f6908083f26f39a79009d608efd5cd
- name: github.com/kr/logfmt
version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
- name: github.com/matttproud/golang_protobuf_extensions
version: c12348ce28de40eed0136aa2b644d0ee0650e56c
subpackages:
- pbutil
- name: github.com/opentracing/opentracing-go
version: 0c3154a3c2ce79d3271985848659870599dfb77c
subpackages:
- ext
- log
- name: github.com/openzipkin/zipkin-go-opentracing
version: e877b8e9d1069eaedfe4f27e9b6a51cfe20073c9
subpackages:
- _thrift/gen-go/scribe
- _thrift/gen-go/zipkincore
- flag
- types
- wire
- name: github.com/pierrec/lz4
version: 5c9560bfa9ace2bf86080bf40d46b34ae44604df
- name: github.com/pierrec/xxHash
version: 5a004441f897722c627870a981d02b29924215fa
subpackages:
- xxHash32
- name: github.com/prometheus/client_golang
version: 5636dc67ae776adf5590da7349e70fbb9559972d
subpackages:
......@@ -44,22 +89,30 @@ imports:
version: 9a94032291f2192936512bab367bc45e77990d6a
subpackages:
- expfmt
- model
- internal/bitbucket.org/ww/goautoneg
- model
- name: github.com/prometheus/procfs
version: abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
- name: github.com/rcrowley/go-metrics
version: 1f30fe9094a513ce4c700b9a54458bbb0c96996c
- name: github.com/Shopify/sarama
version: 9418d7c7189af6ea2df537692f9428efe1368368
- name: golang.org/x/net
version: 7394c112eae4dba7e96bfcfe738e6373d61772b4
subpackages:
- context
- context/ctxhttp
- name: google.golang.org/grpc
version: 09aecb094ef6b9ebe49dd999f52c505beeeb402e
subpackages:
- metadata
- name: gopkg.in/mgo.v2
version: 3f83fa5005286a7fe593b055f0d7771a7dce4655
subpackages:
- bson
- internal/json
- internal/sasl
- internal/scram
- internal/json
- name: gopkg.in/tomb.v2
version: 14b3d72120e8d10ea6e6b7f87f7175734b1faab8
devImports: []
testImports: []
......@@ -6,8 +6,11 @@ import:
- log
- metrics
- metrics/prometheus
- tracing/opentracing
- transport/http
- package: github.com/gorilla/mux
- package: github.com/opentracing/opentracing-go
- package: github.com/openzipkin/zipkin-go-opentracing
- package: github.com/prometheus/client_golang
subpackages:
- prometheus
......@@ -19,4 +22,3 @@ import:
subpackages:
- bson
- package: gopkg.in/tomb.v2
......@@ -15,15 +15,26 @@ import (
"github.com/microservices-demo/user/api"
"github.com/microservices-demo/user/db"
"github.com/microservices-demo/user/db/mongodb"
stdopentracing "github.com/opentracing/opentracing-go"
zipkin "github.com/openzipkin/zipkin-go-opentracing"
stdprometheus "github.com/prometheus/client_golang/prometheus"
"golang.org/x/net/context"
)
var dev bool
var port string
var acc string
var (
dev bool
port string
acc string
zip string
)
const (
ServiceName = "user"
)
func init() {
flag.StringVar(&zip, "zipkin", os.Getenv("ZIPKIN"), "Zipkin address")
flag.StringVar(&port, "port", "8084", "Port on which to run")
db.Register("mongodb", &mongodb.Mongo{})
}
......@@ -42,6 +53,32 @@ func main() {
logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC)
logger = log.NewContext(logger).With("caller", log.DefaultCaller)
}
var tracer stdopentracing.Tracer
{
if zip == "" {
tracer = stdopentracing.NoopTracer{}
} else {
logger := log.NewContext(logger).With("tracer", "Zipkin")
logger.Log("addr", zip)
collector, err := zipkin.NewHTTPCollector(
zip,
zipkin.HTTPLogger(logger),
)
if err != nil {
logger.Log("err", err)
os.Exit(1)
}
tracer, err = zipkin.NewTracer(
zipkin.NewRecorder(collector, false, fmt.Sprintf("localhost:%v", port), ServiceName),
)
if err != nil {
logger.Log("err", err)
os.Exit(1)
}
}
stdopentracing.InitGlobalTracer(tracer)
}
dbconn := false
for !dbconn {
err := db.Init()
......@@ -81,12 +118,12 @@ func main() {
}
// Endpoint domain.
endpoints := api.MakeEndpoints(service)
endpoints := api.MakeEndpoints(service, tracer)
// Create and launch the HTTP server.
go func() {
logger.Log("transport", "HTTP", "port", port)
handler := api.MakeHTTPHandler(ctx, endpoints, logger)
handler := api.MakeHTTPHandler(ctx, endpoints, logger, tracer)
errc <- http.ListenAndServe(fmt.Sprintf(":%v", port), handler)
}()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment