mirror of
https://github.com/go-kit/kit.git
synced 2025-07-17 01:12:38 +02:00
examples/apigateway: refactor
This commit is contained in:
@ -47,7 +47,7 @@ func main() {
|
|||||||
)
|
)
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// Logging domain
|
// Logging domain.
|
||||||
var logger log.Logger
|
var logger log.Logger
|
||||||
{
|
{
|
||||||
logger = log.NewLogfmtLogger(os.Stdout)
|
logger = log.NewLogfmtLogger(os.Stdout)
|
||||||
@ -57,10 +57,10 @@ func main() {
|
|||||||
logger.Log("msg", "hello")
|
logger.Log("msg", "hello")
|
||||||
defer logger.Log("msg", "goodbye")
|
defer logger.Log("msg", "goodbye")
|
||||||
|
|
||||||
// Metrics domain
|
// Metrics domain.
|
||||||
var ints, chars metrics.Counter
|
var ints, chars metrics.Counter
|
||||||
{
|
{
|
||||||
// Business level metrics
|
// Business level metrics.
|
||||||
ints = prometheus.NewCounter(stdprometheus.CounterOpts{
|
ints = prometheus.NewCounter(stdprometheus.CounterOpts{
|
||||||
Namespace: "addsvc",
|
Namespace: "addsvc",
|
||||||
Name: "integers_summed",
|
Name: "integers_summed",
|
||||||
@ -74,7 +74,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
var duration metrics.TimeHistogram
|
var duration metrics.TimeHistogram
|
||||||
{
|
{
|
||||||
// Transport level metrics
|
// Transport level metrics.
|
||||||
duration = metrics.NewTimeHistogram(time.Nanosecond, prometheus.NewSummary(stdprometheus.SummaryOpts{
|
duration = metrics.NewTimeHistogram(time.Nanosecond, prometheus.NewSummary(stdprometheus.SummaryOpts{
|
||||||
Namespace: "addsvc",
|
Namespace: "addsvc",
|
||||||
Name: "request_duration_ns",
|
Name: "request_duration_ns",
|
||||||
@ -82,7 +82,7 @@ func main() {
|
|||||||
}, []string{"method", "success"}))
|
}, []string{"method", "success"}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tracing domain
|
// Tracing domain.
|
||||||
var tracer stdopentracing.Tracer
|
var tracer stdopentracing.Tracer
|
||||||
{
|
{
|
||||||
if *zipkinAddr != "" {
|
if *zipkinAddr != "" {
|
||||||
@ -121,7 +121,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Business domain
|
// Business domain.
|
||||||
var service addsvc.Service
|
var service addsvc.Service
|
||||||
{
|
{
|
||||||
service = addsvc.NewBasicService()
|
service = addsvc.NewBasicService()
|
||||||
@ -129,7 +129,7 @@ func main() {
|
|||||||
service = addsvc.ServiceInstrumentingMiddleware(ints, chars)(service)
|
service = addsvc.ServiceInstrumentingMiddleware(ints, chars)(service)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Endpoint domain
|
// Endpoint domain.
|
||||||
var sumEndpoint endpoint.Endpoint
|
var sumEndpoint endpoint.Endpoint
|
||||||
{
|
{
|
||||||
sumDuration := duration.With(metrics.Field{Key: "method", Value: "Sum"})
|
sumDuration := duration.With(metrics.Field{Key: "method", Value: "Sum"})
|
||||||
@ -155,18 +155,18 @@ func main() {
|
|||||||
ConcatEndpoint: concatEndpoint,
|
ConcatEndpoint: concatEndpoint,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mechanical domain
|
// Mechanical domain.
|
||||||
errc := make(chan error)
|
errc := make(chan error)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// Interrupt handler
|
// Interrupt handler.
|
||||||
go func() {
|
go func() {
|
||||||
c := make(chan os.Signal, 1)
|
c := make(chan os.Signal, 1)
|
||||||
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
|
||||||
errc <- fmt.Errorf("%s", <-c)
|
errc <- fmt.Errorf("%s", <-c)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Debug listener
|
// Debug listener.
|
||||||
go func() {
|
go func() {
|
||||||
logger := log.NewContext(logger).With("transport", "debug")
|
logger := log.NewContext(logger).With("transport", "debug")
|
||||||
|
|
||||||
@ -182,7 +182,7 @@ func main() {
|
|||||||
errc <- http.ListenAndServe(*debugAddr, m)
|
errc <- http.ListenAndServe(*debugAddr, m)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// HTTP transport
|
// HTTP transport.
|
||||||
go func() {
|
go func() {
|
||||||
logger := log.NewContext(logger).With("transport", "HTTP")
|
logger := log.NewContext(logger).With("transport", "HTTP")
|
||||||
h := addsvc.MakeHTTPHandler(ctx, endpoints, tracer, logger)
|
h := addsvc.MakeHTTPHandler(ctx, endpoints, tracer, logger)
|
||||||
@ -190,7 +190,7 @@ func main() {
|
|||||||
errc <- http.ListenAndServe(*httpAddr, h)
|
errc <- http.ListenAndServe(*httpAddr, h)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// gRPC transport
|
// gRPC transport.
|
||||||
go func() {
|
go func() {
|
||||||
logger := log.NewContext(logger).With("transport", "gRPC")
|
logger := log.NewContext(logger).With("transport", "gRPC")
|
||||||
|
|
||||||
@ -208,7 +208,7 @@ func main() {
|
|||||||
errc <- s.Serve(ln)
|
errc <- s.Serve(ln)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Thrift transport
|
// Thrift transport.
|
||||||
go func() {
|
go func() {
|
||||||
logger := log.NewContext(logger).With("transport", "Thrift")
|
logger := log.NewContext(logger).With("transport", "Thrift")
|
||||||
|
|
||||||
@ -252,6 +252,6 @@ func main() {
|
|||||||
).Serve()
|
).Serve()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Run
|
// Run!
|
||||||
logger.Log("exit", <-errc)
|
logger.Log("exit", <-errc)
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
stdlog "log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@ -17,16 +17,18 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/hashicorp/consul/api"
|
"github.com/hashicorp/consul/api"
|
||||||
"github.com/opentracing/opentracing-go"
|
stdopentracing "github.com/opentracing/opentracing-go"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
"github.com/go-kit/kit/endpoint"
|
"github.com/go-kit/kit/endpoint"
|
||||||
"github.com/go-kit/kit/examples/addsvc/client/grpc"
|
"github.com/go-kit/kit/examples/addsvc"
|
||||||
"github.com/go-kit/kit/examples/addsvc/server"
|
addsvcgrpcclient "github.com/go-kit/kit/examples/addsvc/client/grpc"
|
||||||
"github.com/go-kit/kit/loadbalancer"
|
|
||||||
"github.com/go-kit/kit/loadbalancer/consul"
|
|
||||||
"github.com/go-kit/kit/log"
|
"github.com/go-kit/kit/log"
|
||||||
|
"github.com/go-kit/kit/sd"
|
||||||
|
consulsd "github.com/go-kit/kit/sd/consul"
|
||||||
|
"github.com/go-kit/kit/sd/lb"
|
||||||
httptransport "github.com/go-kit/kit/transport/http"
|
httptransport "github.com/go-kit/kit/transport/http"
|
||||||
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -38,13 +40,17 @@ func main() {
|
|||||||
)
|
)
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// Log domain
|
// Logging domain.
|
||||||
logger := log.NewLogfmtLogger(os.Stderr)
|
var logger log.Logger
|
||||||
logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC).With("caller", log.DefaultCaller)
|
{
|
||||||
stdlog.SetFlags(0) // flags are handled by Go kit's logger
|
logger = log.NewLogfmtLogger(os.Stderr)
|
||||||
stdlog.SetOutput(log.NewStdlibAdapter(logger)) // redirect anything using stdlib log to us
|
logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC)
|
||||||
|
logger = log.NewContext(logger).With("caller", log.DefaultCaller)
|
||||||
|
}
|
||||||
|
|
||||||
// Service discovery domain. In this example we use Consul.
|
// Service discovery domain. In this example we use Consul.
|
||||||
|
var client consulsd.Client
|
||||||
|
{
|
||||||
consulConfig := api.DefaultConfig()
|
consulConfig := api.DefaultConfig()
|
||||||
if len(*consulAddr) > 0 {
|
if len(*consulAddr) > 0 {
|
||||||
consulConfig.Address = *consulAddr
|
consulConfig.Address = *consulAddr
|
||||||
@ -54,137 +60,223 @@ func main() {
|
|||||||
logger.Log("err", err)
|
logger.Log("err", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
discoveryClient := consul.NewClient(consulClient)
|
client = consulsd.NewClient(consulClient)
|
||||||
|
}
|
||||||
|
|
||||||
// Context domain.
|
// Transport domain.
|
||||||
|
tracer := stdopentracing.GlobalTracer() // no-op
|
||||||
ctx := context.Background()
|
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()
|
r := mux.NewRouter()
|
||||||
for consulName, methods := range map[string][]struct {
|
|
||||||
path string
|
// Now we begin installing the routes. Each route corresponds to a single
|
||||||
factory loadbalancer.Factory
|
// method: sum, concat, uppercase, and count.
|
||||||
}{
|
|
||||||
"addsvc": {
|
// addsvc routes.
|
||||||
{path: "/api/addsvc/concat", factory: grpc.MakeConcatEndpointFactory(opentracing.GlobalTracer(), nil)},
|
{
|
||||||
{path: "/api/addsvc/sum", factory: grpc.MakeSumEndpointFactory(opentracing.GlobalTracer(), nil)},
|
// Each method gets constructed with a factory. Factories take an
|
||||||
},
|
// instance string, and return a specific endpoint. In the factory we
|
||||||
"stringsvc": {
|
// dial the instance string we get from Consul, and then leverage an
|
||||||
{path: "/api/stringsvc/uppercase", factory: httpFactory(ctx, "GET", "uppercase/")},
|
// addsvc client package to construct a complete service. We can then
|
||||||
{path: "/api/stringsvc/concat", factory: httpFactory(ctx, "GET", "concat/")},
|
// leverage the addsvc.Make{Sum,Concat}Endpoint constructors to convert
|
||||||
},
|
// the complete service to specific endpoint.
|
||||||
} {
|
|
||||||
for _, method := range methods {
|
var (
|
||||||
publisher, err := consul.NewPublisher(discoveryClient, method.factory, logger, consulName)
|
tags = []string{}
|
||||||
if err != nil {
|
passingOnly = true
|
||||||
logger.Log("service", consulName, "path", method.path, "err", err)
|
endpoints = addsvc.Endpoints{}
|
||||||
continue
|
)
|
||||||
}
|
{
|
||||||
lb := loadbalancer.NewRoundRobin(publisher)
|
factory := addsvcFactory(addsvc.MakeSumEndpoint, tracer, logger)
|
||||||
e := loadbalancer.Retry(*retryMax, *retryTimeout, lb)
|
subscriber := consulsd.NewSubscriber(client, factory, logger, "addsvc", tags, passingOnly)
|
||||||
h := makeHandler(ctx, e, logger)
|
balancer := lb.NewRoundRobin(subscriber)
|
||||||
r.HandleFunc(method.path, h)
|
retry := lb.Retry(*retryMax, *retryTimeout, balancer)
|
||||||
|
endpoints.SumEndpoint = retry
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
factory := addsvcFactory(addsvc.MakeConcatEndpoint, tracer, logger)
|
||||||
|
subscriber := consulsd.NewSubscriber(client, factory, logger, "addsvc", tags, passingOnly)
|
||||||
|
balancer := lb.NewRoundRobin(subscriber)
|
||||||
|
retry := lb.Retry(*retryMax, *retryTimeout, balancer)
|
||||||
|
endpoints.ConcatEndpoint = retry
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mechanical stuff.
|
// Here we leverage the fact that addsvc comes with a constructor for an
|
||||||
|
// HTTP handler, and just install it under a particular path prefix in
|
||||||
|
// our router.
|
||||||
|
|
||||||
|
r.PathPrefix("addsvc/").Handler(addsvc.MakeHTTPHandler(ctx, endpoints, tracer, logger))
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringsvc routes.
|
||||||
|
{
|
||||||
|
// addsvc had lots of nice importable Go packages we could leverage.
|
||||||
|
// With stringsvc we are not so fortunate, it just has some endpoints
|
||||||
|
// that we assume will exist. So we have to write that logic here. This
|
||||||
|
// is by design, so you can see two totally different methods of
|
||||||
|
// proxying to a remote service.
|
||||||
|
|
||||||
|
var (
|
||||||
|
tags = []string{}
|
||||||
|
passingOnly = true
|
||||||
|
uppercase endpoint.Endpoint
|
||||||
|
count endpoint.Endpoint
|
||||||
|
)
|
||||||
|
{
|
||||||
|
factory := stringsvcFactory(ctx, "GET", "/uppercase")
|
||||||
|
subscriber := consulsd.NewSubscriber(client, factory, logger, "stringsvc", tags, passingOnly)
|
||||||
|
balancer := lb.NewRoundRobin(subscriber)
|
||||||
|
retry := lb.Retry(*retryMax, *retryTimeout, balancer)
|
||||||
|
uppercase = retry
|
||||||
|
}
|
||||||
|
{
|
||||||
|
factory := stringsvcFactory(ctx, "GET", "/count")
|
||||||
|
subscriber := consulsd.NewSubscriber(client, factory, logger, "stringsvc", tags, passingOnly)
|
||||||
|
balancer := lb.NewRoundRobin(subscriber)
|
||||||
|
retry := lb.Retry(*retryMax, *retryTimeout, balancer)
|
||||||
|
count = retry
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can use the transport/http.Server to act as our handler, all we
|
||||||
|
// have to do provide it with the encode and decode functions for our
|
||||||
|
// stringsvc methods.
|
||||||
|
|
||||||
|
r.Handle("/stringsvc/uppercase", httptransport.NewServer(ctx, uppercase, decodeUppercaseRequest, encodeJSONResponse))
|
||||||
|
r.Handle("/stringsvc/count", httptransport.NewServer(ctx, count, decodeCountRequest, encodeJSONResponse))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interrupt handler.
|
||||||
errc := make(chan error)
|
errc := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
errc <- interrupt()
|
c := make(chan os.Signal)
|
||||||
|
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
errc <- fmt.Errorf("%s", <-c)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// HTTP transport.
|
||||||
go func() {
|
go func() {
|
||||||
logger.Log("transport", "http", "addr", *httpAddr)
|
logger.Log("transport", "HTTP", "addr", *httpAddr)
|
||||||
errc <- http.ListenAndServe(*httpAddr, r)
|
errc <- http.ListenAndServe(*httpAddr, r)
|
||||||
}()
|
}()
|
||||||
logger.Log("err", <-errc)
|
|
||||||
|
// Run!
|
||||||
|
logger.Log("exit", <-errc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeHandler(ctx context.Context, e endpoint.Endpoint, logger log.Logger) http.HandlerFunc {
|
func addsvcFactory(makeEndpoint func(addsvc.Service) endpoint.Endpoint, tracer stdopentracing.Tracer, logger log.Logger) sd.Factory {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
resp, err := e(ctx, r.Body)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log("err", err)
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b, ok := resp.([]byte)
|
|
||||||
if !ok {
|
|
||||||
logger.Log("err", "endpoint response is not of type []byte")
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = w.Write(b)
|
|
||||||
if err != nil {
|
|
||||||
logger.Log("err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeSumEndpoint(svc server.AddService) endpoint.Endpoint {
|
|
||||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
|
||||||
r := request.(io.Reader)
|
|
||||||
var req server.SumRequest
|
|
||||||
if err := json.NewDecoder(r).Decode(&req); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
v := svc.Sum(req.A, req.B)
|
|
||||||
return json.Marshal(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeConcatEndpoint(svc server.AddService) endpoint.Endpoint {
|
|
||||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
|
||||||
r := request.(io.Reader)
|
|
||||||
var req server.ConcatRequest
|
|
||||||
if err := json.NewDecoder(r).Decode(&req); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
v := svc.Concat(req.A, req.B)
|
|
||||||
return json.Marshal(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func httpFactory(ctx context.Context, method, path string) loadbalancer.Factory {
|
|
||||||
return func(instance string) (endpoint.Endpoint, io.Closer, error) {
|
return func(instance string) (endpoint.Endpoint, io.Closer, error) {
|
||||||
var e endpoint.Endpoint
|
// We could just as easily use the HTTP or Thrift client package to make
|
||||||
if !strings.HasPrefix(instance, "http") {
|
// the connection to addsvc. We've chosen gRPC arbitrarily. Note that
|
||||||
instance = "http://" + instance
|
// the transport is an implementation detail: it doesn't leak out of
|
||||||
}
|
// this function. Nice!
|
||||||
u, err := url.Parse(instance)
|
|
||||||
|
conn, err := grpc.Dial(instance, grpc.WithInsecure())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
u.Path = path
|
service := addsvcgrpcclient.New(conn, tracer, logger)
|
||||||
|
endpoint := makeEndpoint(service)
|
||||||
|
|
||||||
e = httptransport.NewClient(method, u, passEncode, passDecode).Endpoint()
|
// Notice that the addsvc gRPC client converts the connection to a
|
||||||
return e, nil, nil
|
// complete addsvc, and we just throw away everything except the method
|
||||||
|
// we're interested in. A smarter factory would mux multiple methods
|
||||||
|
// over the same connection. But that would require more work to manage
|
||||||
|
// the returned io.Closer, e.g. reference counting. Since this is for
|
||||||
|
// the purposes of demonstration, we'll just keep it simple.
|
||||||
|
|
||||||
|
return endpoint, conn, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func passEncode(_ context.Context, r *http.Request, request interface{}) error {
|
func stringsvcFactory(ctx context.Context, method, path string) sd.Factory {
|
||||||
r.Body = request.(io.ReadCloser)
|
return func(instance string) (endpoint.Endpoint, io.Closer, error) {
|
||||||
|
if !strings.HasPrefix(instance, "http") {
|
||||||
|
instance = "http://" + instance
|
||||||
|
}
|
||||||
|
tgt, err := url.Parse(instance)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
tgt.Path = path
|
||||||
|
|
||||||
|
// Since stringsvc doesn't have any kind of package we can import, or
|
||||||
|
// any formal spec, we are forced to just assert where the endpoints
|
||||||
|
// live, and write our own code to encode and decode requests and
|
||||||
|
// responses. Ideally, if you write the service, you will want to
|
||||||
|
// provide stronger guarantees to your clients.
|
||||||
|
|
||||||
|
var (
|
||||||
|
enc httptransport.EncodeRequestFunc
|
||||||
|
dec httptransport.DecodeResponseFunc
|
||||||
|
)
|
||||||
|
switch path {
|
||||||
|
case "/uppercase":
|
||||||
|
enc, dec = encodeJSONRequest, decodeUppercaseResponse
|
||||||
|
case "/count":
|
||||||
|
enc, dec = encodeJSONRequest, decodeCountResponse
|
||||||
|
default:
|
||||||
|
return nil, nil, fmt.Errorf("unknown stringsvc path %q", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return httptransport.NewClient(method, tgt, enc, dec).Endpoint(), nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeJSONRequest(_ context.Context, req *http.Request, request interface{}) error {
|
||||||
|
// Both uppercase and count requests are encoded in the same way:
|
||||||
|
// simple JSON serialization to the request body.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := json.NewEncoder(&buf).Encode(request); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Body = ioutil.NopCloser(&buf)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func passDecode(_ context.Context, r *http.Response) (interface{}, error) {
|
func encodeJSONResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
|
||||||
return ioutil.ReadAll(r.Body)
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
return json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func interrupt() error {
|
// I've just copied these functions from stringsvc3/transport.go, inlining the
|
||||||
c := make(chan os.Signal)
|
// struct definitions.
|
||||||
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
return fmt.Errorf("%s", <-c)
|
func decodeUppercaseResponse(ctx context.Context, resp *http.Response) (interface{}, error) {
|
||||||
|
var response struct {
|
||||||
|
V string `json:"v"`
|
||||||
|
Err string `json:"err,omitempty"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeCountResponse(ctx context.Context, resp *http.Response) (interface{}, error) {
|
||||||
|
var response struct {
|
||||||
|
V int `json:"v"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeUppercaseRequest(ctx context.Context, req *http.Request) (interface{}, error) {
|
||||||
|
var request struct {
|
||||||
|
S string `json:"s"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return request, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeCountRequest(ctx context.Context, req *http.Request) (interface{}, error) {
|
||||||
|
var request struct {
|
||||||
|
S string `json:"s"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return request, nil
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ var _ sd.Subscriber = &Subscriber{}
|
|||||||
// NewSubscriber returns a Consul subscriber which returns endpoints for the
|
// NewSubscriber returns a Consul subscriber which returns endpoints for the
|
||||||
// requested service. It only returns instances for which all of the passed tags
|
// requested service. It only returns instances for which all of the passed tags
|
||||||
// are present.
|
// are present.
|
||||||
func NewSubscriber(client Client, factory sd.Factory, logger log.Logger, service string, tags []string, passingOnly bool) (*Subscriber, error) {
|
func NewSubscriber(client Client, factory sd.Factory, logger log.Logger, service string, tags []string, passingOnly bool) *Subscriber {
|
||||||
s := &Subscriber{
|
s := &Subscriber{
|
||||||
cache: cache.New(factory, logger),
|
cache: cache.New(factory, logger),
|
||||||
client: client,
|
client: client,
|
||||||
@ -52,7 +52,7 @@ func NewSubscriber(client Client, factory sd.Factory, logger log.Logger, service
|
|||||||
|
|
||||||
s.cache.Update(instances)
|
s.cache.Update(instances)
|
||||||
go s.loop(index)
|
go s.loop(index)
|
||||||
return s, nil
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Endpoints implements the Subscriber interface.
|
// Endpoints implements the Subscriber interface.
|
||||||
|
@ -63,10 +63,7 @@ func TestSubscriber(t *testing.T) {
|
|||||||
client = newTestClient(consulState)
|
client = newTestClient(consulState)
|
||||||
)
|
)
|
||||||
|
|
||||||
s, err := NewSubscriber(client, testFactory, logger, "search", []string{"api"}, true)
|
s := NewSubscriber(client, testFactory, logger, "search", []string{"api"}, true)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer s.Stop()
|
defer s.Stop()
|
||||||
|
|
||||||
endpoints, err := s.Endpoints()
|
endpoints, err := s.Endpoints()
|
||||||
@ -85,10 +82,7 @@ func TestSubscriberNoService(t *testing.T) {
|
|||||||
client = newTestClient(consulState)
|
client = newTestClient(consulState)
|
||||||
)
|
)
|
||||||
|
|
||||||
s, err := NewSubscriber(client, testFactory, logger, "feed", []string{}, true)
|
s := NewSubscriber(client, testFactory, logger, "feed", []string{}, true)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer s.Stop()
|
defer s.Stop()
|
||||||
|
|
||||||
endpoints, err := s.Endpoints()
|
endpoints, err := s.Endpoints()
|
||||||
@ -107,10 +101,7 @@ func TestSubscriberWithTags(t *testing.T) {
|
|||||||
client = newTestClient(consulState)
|
client = newTestClient(consulState)
|
||||||
)
|
)
|
||||||
|
|
||||||
s, err := NewSubscriber(client, testFactory, logger, "search", []string{"api", "v2"}, true)
|
s := NewSubscriber(client, testFactory, logger, "search", []string{"api", "v2"}, true)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer s.Stop()
|
defer s.Stop()
|
||||||
|
|
||||||
endpoints, err := s.Endpoints()
|
endpoints, err := s.Endpoints()
|
||||||
@ -124,10 +115,7 @@ func TestSubscriberWithTags(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSubscriberAddressOverride(t *testing.T) {
|
func TestSubscriberAddressOverride(t *testing.T) {
|
||||||
s, err := NewSubscriber(newTestClient(consulState), testFactory, log.NewNopLogger(), "search", []string{"db"}, true)
|
s := NewSubscriber(newTestClient(consulState), testFactory, log.NewNopLogger(), "search", []string{"db"}, true)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer s.Stop()
|
defer s.Stop()
|
||||||
|
|
||||||
endpoints, err := s.Endpoints()
|
endpoints, err := s.Endpoints()
|
||||||
|
@ -9,7 +9,8 @@ import (
|
|||||||
// Factory is a function that converts an instance string (e.g. host:port) to a
|
// Factory is a function that converts an instance string (e.g. host:port) to a
|
||||||
// specific endpoint. Instances that provide multiple endpoints require multiple
|
// specific endpoint. Instances that provide multiple endpoints require multiple
|
||||||
// factories. A factory also returns an io.Closer that's invoked when the
|
// factories. A factory also returns an io.Closer that's invoked when the
|
||||||
// instance goes away and needs to be cleaned up.
|
// instance goes away and needs to be cleaned up. Factories may return nil
|
||||||
|
// closers.
|
||||||
//
|
//
|
||||||
// Users are expected to provide their own factory functions that assume
|
// Users are expected to provide their own factory functions that assume
|
||||||
// specific transports, or can deduce transports by parsing the instance string.
|
// specific transports, or can deduce transports by parsing the instance string.
|
||||||
|
Reference in New Issue
Block a user