165 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			165 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package addsvc
 | |
| 
 | |
| // This file contains the Service definition, and a basic service
 | |
| // implementation. It also includes service middlewares.
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"time"
 | |
| 
 | |
| 	"golang.org/x/net/context"
 | |
| 
 | |
| 	"github.com/go-kit/kit/log"
 | |
| 	"github.com/go-kit/kit/metrics"
 | |
| )
 | |
| 
 | |
| // Service describes a service that adds things together.
 | |
| type Service interface {
 | |
| 	Sum(ctx context.Context, a, b int) (int, error)
 | |
| 	Concat(ctx context.Context, a, b string) (string, error)
 | |
| }
 | |
| 
 | |
| // Business-domain errors like these may be served in two ways: returned
 | |
| // directly by endpoints, or bundled into the response struct. Both methods can
 | |
| // be made to work, but errors returned directly by endpoints are counted by
 | |
| // middlewares that check errors, like circuit breakers.
 | |
| //
 | |
| // If you don't want that behavior -- and you probably don't -- then it's better
 | |
| // to bundle errors into the response struct.
 | |
| 
 | |
| var (
 | |
| 	// ErrTwoZeroes is an arbitrary business rule for the Add method.
 | |
| 	ErrTwoZeroes = errors.New("can't sum two zeroes")
 | |
| 
 | |
| 	// ErrIntOverflow protects the Add method. We've decided that this error
 | |
| 	// indicates a misbehaving service and should count against e.g. circuit
 | |
| 	// breakers. So, we return it directly in endpoints, to illustrate the
 | |
| 	// difference. In a real service, this probably wouldn't be the case.
 | |
| 	ErrIntOverflow = errors.New("integer overflow")
 | |
| 
 | |
| 	// ErrMaxSizeExceeded protects the Concat method.
 | |
| 	ErrMaxSizeExceeded = errors.New("result exceeds maximum size")
 | |
| )
 | |
| 
 | |
| // These annoying helper functions are required to translate Go error types to
 | |
| // and from strings, which is the type we use in our IDLs to represent errors.
 | |
| // There is special casing to treat empty strings as nil errors.
 | |
| 
 | |
| func str2err(s string) error {
 | |
| 	if s == "" {
 | |
| 		return nil
 | |
| 	}
 | |
| 	return errors.New(s)
 | |
| }
 | |
| 
 | |
| func err2str(err error) string {
 | |
| 	if err == nil {
 | |
| 		return ""
 | |
| 	}
 | |
| 	return err.Error()
 | |
| }
 | |
| 
 | |
| // NewBasicService returns a naïve, stateless implementation of Service.
 | |
| func NewBasicService() Service {
 | |
| 	return basicService{}
 | |
| }
 | |
| 
 | |
| type basicService struct{}
 | |
| 
 | |
| const (
 | |
| 	intMax = 1<<31 - 1
 | |
| 	intMin = -(intMax + 1)
 | |
| 	maxLen = 102400
 | |
| )
 | |
| 
 | |
| // Sum implements Service.
 | |
| func (s basicService) Sum(_ context.Context, a, b int) (int, error) {
 | |
| 	if a == 0 && b == 0 {
 | |
| 		return 0, ErrTwoZeroes
 | |
| 	}
 | |
| 	if (b > 0 && a > (intMax-b)) || (b < 0 && a < (intMin-b)) {
 | |
| 		return 0, ErrIntOverflow
 | |
| 	}
 | |
| 	return a + b, nil
 | |
| }
 | |
| 
 | |
| // Concat implements Service.
 | |
| func (s basicService) Concat(_ context.Context, a, b string) (string, error) {
 | |
| 	if len(a)+len(b) > maxLen {
 | |
| 		return "", ErrMaxSizeExceeded
 | |
| 	}
 | |
| 	return a + b, nil
 | |
| }
 | |
| 
 | |
| // Middleware describes a service (as opposed to endpoint) middleware.
 | |
| type Middleware func(Service) Service
 | |
| 
 | |
| // ServiceLoggingMiddleware returns a service middleware that logs the
 | |
| // parameters and result of each method invocation.
 | |
| func ServiceLoggingMiddleware(logger log.Logger) Middleware {
 | |
| 	return func(next Service) Service {
 | |
| 		return serviceLoggingMiddleware{
 | |
| 			logger: logger,
 | |
| 			next:   next,
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type serviceLoggingMiddleware struct {
 | |
| 	logger log.Logger
 | |
| 	next   Service
 | |
| }
 | |
| 
 | |
| func (mw serviceLoggingMiddleware) Sum(ctx context.Context, a, b int) (v int, err error) {
 | |
| 	defer func(begin time.Time) {
 | |
| 		mw.logger.Log(
 | |
| 			"method", "Sum",
 | |
| 			"a", a, "b", b, "result", v, "error", err,
 | |
| 			"took", time.Since(begin),
 | |
| 		)
 | |
| 	}(time.Now())
 | |
| 	return mw.next.Sum(ctx, a, b)
 | |
| }
 | |
| 
 | |
| func (mw serviceLoggingMiddleware) Concat(ctx context.Context, a, b string) (v string, err error) {
 | |
| 	defer func(begin time.Time) {
 | |
| 		mw.logger.Log(
 | |
| 			"method", "Concat",
 | |
| 			"a", a, "b", b, "result", v, "error", err,
 | |
| 			"took", time.Since(begin),
 | |
| 		)
 | |
| 	}(time.Now())
 | |
| 	return mw.next.Concat(ctx, a, b)
 | |
| }
 | |
| 
 | |
| // ServiceInstrumentingMiddleware returns a service middleware that instruments
 | |
| // the number of integers summed and characters concatenated over the lifetime of
 | |
| // the service.
 | |
| func ServiceInstrumentingMiddleware(ints, chars metrics.Counter) Middleware {
 | |
| 	return func(next Service) Service {
 | |
| 		return serviceInstrumentingMiddleware{
 | |
| 			ints:  ints,
 | |
| 			chars: chars,
 | |
| 			next:  next,
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type serviceInstrumentingMiddleware struct {
 | |
| 	ints  metrics.Counter
 | |
| 	chars metrics.Counter
 | |
| 	next  Service
 | |
| }
 | |
| 
 | |
| func (mw serviceInstrumentingMiddleware) Sum(ctx context.Context, a, b int) (int, error) {
 | |
| 	v, err := mw.next.Sum(ctx, a, b)
 | |
| 	mw.ints.Add(float64(v))
 | |
| 	return v, err
 | |
| }
 | |
| 
 | |
| func (mw serviceInstrumentingMiddleware) Concat(ctx context.Context, a, b string) (string, error) {
 | |
| 	v, err := mw.next.Concat(ctx, a, b)
 | |
| 	mw.chars.Add(float64(len(v)))
 | |
| 	return v, err
 | |
| }
 |