func main() { var ( httpAddr = flag.String("http.addr", ":8000", "Address for HTTP (JSON) server") consulAddr = flag.String("consul.addr", "", "Consul agent address") retryMax = flag.Int("retry.max", 3, "per-request retries to different instances") retryTimeout = flag.Duration("retry.timeout", 500*time.Millisecond, "per-request timeout, including retries") ) flag.Parse() // Log domain logger := log.NewLogfmtLogger(os.Stderr) logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC).With("caller", log.DefaultCaller) stdlog.SetFlags(0) // flags are handled by Go kit's logger stdlog.SetOutput(log.NewStdlibAdapter(logger)) // redirect anything using stdlib log to us // Service discovery domain. In this example we use Consul. consulConfig := api.DefaultConfig() if len(*consulAddr) > 0 { consulConfig.Address = *consulAddr } consulClient, err := api.NewClient(consulConfig) if err != nil { logger.Log("err", err) os.Exit(1) } discoveryClient := consul.NewClient(consulClient) // Context domain. ctx := context.Background() // Set up our routes. // // Each Consul service name maps to multiple instances of that service. We // connect to each instance according to its pre-determined transport: in this // case, we choose to access addsvc via its gRPC client, and stringsvc over // plain transport/http (it has no client package). // // Each service instance implements multiple methods, and we want to map each // method to a unique path on the API gateway. So, we define that path and its // corresponding factory function, which takes an instance string and returns an // endpoint.Endpoint for the specific method. // // Finally, we mount that path + endpoint handler into the router. r := mux.NewRouter() for consulName, methods := range map[string][]struct { path string factory loadbalancer.Factory }{ "addsvc": { {path: "/api/addsvc/concat", factory: grpc.MakeConcatEndpointFactory(opentracing.GlobalTracer(), nil)}, {path: "/api/addsvc/sum", factory: grpc.MakeSumEndpointFactory(opentracing.GlobalTracer(), nil)}, }, "stringsvc": { {path: "/api/stringsvc/uppercase", factory: httpFactory(ctx, "GET", "uppercase/")}, {path: "/api/stringsvc/concat", factory: httpFactory(ctx, "GET", "concat/")}, }, } { for _, method := range methods { publisher, err := consul.NewPublisher(discoveryClient, method.factory, logger, consulName) if err != nil { logger.Log("service", consulName, "path", method.path, "err", err) continue } lb := loadbalancer.NewRoundRobin(publisher) e := loadbalancer.Retry(*retryMax, *retryTimeout, lb) h := makeHandler(ctx, e, logger) r.HandleFunc(method.path, h) } } // Mechanical stuff. errc := make(chan error) go func() { errc <- interrupt() }() go func() { logger.Log("transport", "http", "addr", *httpAddr) errc <- http.ListenAndServe(*httpAddr, r) }() logger.Log("err", <-errc) }
func main() { var ( transport = flag.String("transport", "httpjson", "httpjson, grpc, netrpc, thrift") httpAddrs = flag.String("http.addrs", "localhost:8001", "Comma-separated list of addresses for HTTP (JSON) servers") grpcAddrs = flag.String("grpc.addrs", "localhost:8002", "Comma-separated list of addresses for gRPC servers") netrpcAddrs = flag.String("netrpc.addrs", "localhost:8003", "Comma-separated list of addresses for net/rpc servers") thriftAddrs = flag.String("thrift.addrs", "localhost:8004", "Comma-separated list of addresses for Thrift servers") thriftProtocol = flag.String("thrift.protocol", "binary", "binary, compact, json, simplejson") thriftBufferSize = flag.Int("thrift.buffer.size", 0, "0 for unbuffered") thriftFramed = flag.Bool("thrift.framed", false, "true to enable framing") // Two OpenTracing backends (to demonstrate how they can be interchanged): appdashAddr = flag.String("appdash.addr", "", "Enable Appdash tracing via an Appdash server host:port") lightstepAccessToken = flag.String("lightstep.token", "", "Enable LightStep tracing via a LightStep access token") ) flag.Parse() if len(os.Args) < 4 { fmt.Fprintf(os.Stderr, "\n%s [flags] method arg1 arg2\n\n", filepath.Base(os.Args[0])) flag.Usage() os.Exit(1) } randomSeed := time.Now().UnixNano() root := context.Background() method, s1, s2 := flag.Arg(0), flag.Arg(1), flag.Arg(2) var logger log.Logger logger = log.NewLogfmtLogger(os.Stdout) logger = log.NewContext(logger).With("caller", log.DefaultCaller) logger = log.NewContext(logger).With("transport", *transport) tracingLogger := log.NewContext(logger).With("component", "tracing") // Set up OpenTracing var tracer opentracing.Tracer { switch { case *appdashAddr != "" && *lightstepAccessToken == "": tracer = appdashot.NewTracer(appdash.NewRemoteCollector(*appdashAddr)) case *appdashAddr == "" && *lightstepAccessToken != "": tracer = lightstep.NewTracer(lightstep.Options{ AccessToken: *lightstepAccessToken, }) defer lightstep.FlushLightStepTracer(tracer) case *appdashAddr == "" && *lightstepAccessToken == "": tracer = opentracing.GlobalTracer() // no-op default: panic("specify either -appdash.addr or -lightstep.access.token, not both") } } var ( instances []string sumFactory, concatFactory loadbalancer.Factory ) switch *transport { case "grpc": instances = strings.Split(*grpcAddrs, ",") sumFactory = grpcclient.MakeSumEndpointFactory(tracer, tracingLogger) concatFactory = grpcclient.MakeConcatEndpointFactory(tracer, tracingLogger) case "httpjson": instances = strings.Split(*httpAddrs, ",") for i, rawurl := range instances { if !strings.HasPrefix("http", rawurl) { instances[i] = "http://" + rawurl } } sumFactory = httpjsonclient.MakeSumEndpointFactory(tracer, tracingLogger) concatFactory = httpjsonclient.MakeConcatEndpointFactory(tracer, tracingLogger) case "netrpc": instances = strings.Split(*netrpcAddrs, ",") sumFactory = netrpcclient.SumEndpointFactory concatFactory = netrpcclient.ConcatEndpointFactory case "thrift": instances = strings.Split(*thriftAddrs, ",") thriftClient := thriftclient.New(*thriftProtocol, *thriftBufferSize, *thriftFramed, logger) sumFactory = thriftClient.SumEndpoint concatFactory = thriftClient.ConcatEndpoint default: logger.Log("err", "invalid transport") os.Exit(1) } sum := buildEndpoint(tracer, "sum", instances, sumFactory, randomSeed, logger) concat := buildEndpoint(tracer, "concat", instances, concatFactory, randomSeed, logger) svc := newClient(root, sum, concat, logger) begin := time.Now() switch method { case "sum": a, _ := strconv.Atoi(s1) b, _ := strconv.Atoi(s2) v := svc.Sum(a, b) logger.Log("method", "sum", "a", a, "b", b, "v", v, "took", time.Since(begin)) case "concat": a, b := s1, s2 v := svc.Concat(a, b) logger.Log("method", "concat", "a", a, "b", b, "v", v, "took", time.Since(begin)) default: logger.Log("err", "invalid method "+method) os.Exit(1) } }