11 "golang.org/x/time/rate"
13 "github.com/sony/gobreaker"
15 "github.com/go-kit/kit/circuitbreaker"
16 "github.com/go-kit/kit/endpoint"
17 "github.com/go-kit/kit/log"
18 "github.com/go-kit/kit/ratelimit"
19 "github.com/go-kit/kit/sd"
20 "github.com/go-kit/kit/sd/lb"
21 httptransport "github.com/go-kit/kit/transport/http"
24 func proxyingMiddleware(ctx context.Context, instances string, logger log.Logger) ServiceMiddleware {
25 // If instances is empty, don't proxy.
27 logger.Log("proxy_to", "none")
28 return func(next StringService) StringService { return next }
31 // Set some parameters for our client.
33 qps = 100 // beyond which we will return an error
34 maxAttempts = 3 // per request, before giving up
35 maxTime = 250 * time.Millisecond // wallclock time, before giving up
38 // Otherwise, construct an endpoint for each instance in the list, and add
39 // it to a fixed set of endpoints. In a real service, rather than doing this
40 // by hand, you'd probably use package sd's support for your service
43 instanceList = split(instances)
44 endpointer sd.FixedEndpointer
46 logger.Log("proxy_to", fmt.Sprint(instanceList))
47 for _, instance := range instanceList {
48 var e endpoint.Endpoint
49 e = makeUppercaseProxy(ctx, instance)
50 e = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(e)
51 e = ratelimit.NewErroringLimiter(rate.NewLimiter(rate.Every(time.Second), qps))(e)
52 endpointer = append(endpointer, e)
55 // Now, build a single, retrying, load-balancing endpoint out of all of
56 // those individual endpoints.
57 balancer := lb.NewRoundRobin(endpointer)
58 retry := lb.Retry(maxAttempts, maxTime, balancer)
60 // And finally, return the ServiceMiddleware, implemented by proxymw.
61 return func(next StringService) StringService {
62 return proxymw{ctx, next, retry}
66 // proxymw implements StringService, forwarding Uppercase requests to the
67 // provided endpoint, and serving all other (i.e. Count) requests via the
68 // next StringService.
71 next StringService // Serve most requests via this service...
72 uppercase endpoint.Endpoint // ...except Uppercase, which gets served by this endpoint
75 func (mw proxymw) Count(s string) int {
76 return mw.next.Count(s)
79 func (mw proxymw) Uppercase(s string) (string, error) {
80 response, err := mw.uppercase(mw.ctx, uppercaseRequest{S: s})
85 resp := response.(uppercaseResponse)
87 return resp.V, errors.New(resp.Err)
92 func makeUppercaseProxy(ctx context.Context, instance string) endpoint.Endpoint {
93 if !strings.HasPrefix(instance, "http") {
94 instance = "http://" + instance
96 u, err := url.Parse(instance)
101 u.Path = "/uppercase"
103 return httptransport.NewClient(
107 decodeUppercaseResponse,
111 func split(s string) []string {
112 a := strings.Split(s, ",")
114 a[i] = strings.TrimSpace(a[i])