kitty

package module
v1.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 26, 2021 License: MIT Imports: 14 Imported by: 1

README

kitty

Travis-CI GoDoc GoReportCard Coverage Status

go get github.com/objenious/kitty

Kitty is a slightly opinionated framework based on go-kit. It's goal is to ease development of microservices deployed on Kubernetes (or any similar orchestration platform).

Kitty has an opinion on:

  • transports: HTTP only (additional transports can be added as long as they implement kitty.Transport, a Google Pub/Sub transport is available as a separate package),
  • errors: an error may be Retryable (e.g. 5XX status codes) or not (e.g. 4XX status codes),
  • status codes: unless specified, request decoding errors will generate 400 HTTP status codes.

Kitty has no opinion on:

  • logging: no logs are generated by default, you can plug your logger and it will get additional context,
  • packages: kitty only imports go-kit and the standard library,
  • routers: you can use any router (a Gorilla Mux implementation is available in a sub-package, other routers can easily be plugged),
  • encoding: use whatever encoding you want (JSON, messagepack, protobuf, ...),
  • monitoring, metrics and tracing: use Istio, a sidecar process or a middleware.

Kitty includes 2 sub-packages:

  • backoff: Retryable-aware exponential backoff (only Retryable errors trigger retries),
  • circuitbreaker: Retryable-aware circuit breaker (only Retryable errors trigger the circuit breaker).

Example

Server-side

t := kitty.NewHTTPTransport(kitty.Config{HTTPPort: 8081}).
  Router(gorilla.Router()).
  Endpoint("POST", "/foo", Foo, kitty.Decoder(decodeFooRequest)).
  Endpoint("GET", "/bar", Bar)

kitty.NewServer(t).Run(ctx)

// Foo is a go-kit Endpoint
func Foo(ctx context.Context, request interface{}) (interface{}, error) {
  fr := request.(fooRequest)
  return fooResponse{Message: fmt.Sprintf("Good morning %s !", fr.Name)}, nil
}

// decodeFooRequest
func decodeFooRequest(ctx context.Context, r *http.Request) (interface{}, error) {
  var request fooRequest
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		return nil, err
	}
	return request, nil
}

Client-side (with circuit breaker & exponential backoff)

u, err := url.Parse("http://example.com/foo")
e := kitty.NewClient(
  "POST",
  u,
  kithttp.EncodeJSONRequest,
  decodeFooResponse
).Endpoint()
cb := gobreaker.NewCircuitBreaker(gobreaker.Settings{Name: "foo"})
e = kittycircuitbreaker.NewCircuitBreaker(cb)(e)
bo := backoff.NewExponentialBackOff()
e = kittybackoff.NewBackoff(bo)(e)

How-to

Log requests
kitty.NewServer(t).
  // Log as JSON
  Logger(log.NewJSONLogger(log.NewSyncWriter(os.Stdout))).
  // Add path and method to all log lines
  LogContext("http-path", "http-method").
  // Log request only if an error occurred
  Middlewares(kitty.LogEndpoint(kitty.LogErrors))
Integrate with Istio

TBD

Integrate liveness/readiness checks

Using github.com/heptiolabs/healthcheck:

health := healthcheck.NewHandler()
health.AddLivenessCheck("goroutine-threshold", healthcheck.GoroutineCountCheck(100))
health.AddReadinessCheck("database", healthcheck.DatabasePingCheck(db, 1*time.Second))

t := kitty.NewTransport(kitty.Config{}).Liveness(health.LiveEndpoint).Readiness(health.ReadyEndpoint)
Use Google Pub/Sub as a transport

https://github.com/objenious/kitty-gcp adds a Google Pub/Sub transport to kitty:

import "github.com/objenious/kitty-gcp/pubsub"

tr := pubsub.NewTransport(ctx, "project-id").
  Endpoint(subscriptionName, endpoint, Decoder(decodeFunc))
err := kitty.NewServer(tr).Run(ctx)

Requirements

Go > 1.11

Contribution guidelines

Contributions are welcome, as long as :

  • unit tests & comments are included,
  • no external package is added to the top-level package (implementations can be added as sub-packages).

Thanks

kitty is heavily inspired by gizmo/kit (https://godoc.org/github.com/NYTimes/gizmo/server/kit), with a different approach to server setup and without the gRPC clutter.

License

MIT - See LICENSE file

Documentation

Overview

Package kitty is a slightly opinionated framework based on go-kit. It's goal is to ease development of services deployed on Kubernetes (or any similar orchestration platform).

Kitty has an opinion on:

* transports: HTTP only (additional transports can be added as long as they implement kitty.Transport), * errors: an error may be Retryable (e.g. 5XX status codes) or not (e.g. 4XX status codes).

Kitty has no opinion on:

* logging: no logs are generated by default, you can plug your logger and it will get additional context,

* packages: kitty only imports go-kit and the standard library,

* routers: you can use any router (Gorilla Mux works out of the box, other routers can easily be plugged),

* encoding: use whatever encoding you want (JSON, messagepack, protobuf, ...),

* monitoring, metrics and tracing: use Istio or a sidecar process.

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultConfig = Config{
	HTTPPort:           8080,
	LivenessCheckPath:  "/alivez",
	ReadinessCheckPath: "/readyz",
	EnablePProf:        false,
	EncodeResponse:     kithttp.EncodeJSONResponse,
}

DefaultConfig defines the default config of kitty.HTTPTransport.

Functions

func HTTPError

func HTTPError(resp *http.Response) error

HTTPError builds an error based on a http.Response. If status code is < 300 or 304, nil is returned. 429, 5XX errors are Retryable.

func IsRetryable

func IsRetryable(err error) bool

IsRetryable checks if an error is retryable (i.e. implements Retryabler and Retryable returns true). Retryable errors may be wrapped using github.com/pkg/errors. If the error is nil or does not implement Retryabler, false is returned.

func LogEndpoint

func LogEndpoint(fields ...LogOption) endpoint.Middleware

LogEndpoint creates a middleware that logs Endpoint calls. If LogRequest is specified, the endpoint request will be logged before the endpoint is called. If LogResponse is specified, the endpoint response will be logged after. If LogErrors is specified, the endpoint request will be logged if the endpoint returns an error. With LogResponse and LogErrors, the endpoint duration and result HTTP status code will be added to logs.

func LogMessage added in v0.0.3

func LogMessage(ctx context.Context, msg string, keyvals ...interface{}) error

LogMessage will log a message. This function can only be called from an endpoint.

func Logger

func Logger(ctx context.Context) log.Logger

Logger will return the logger that has been injected into the context by the kitty server. This function can only be called from an endpoint.

func Retryable added in v0.0.3

func Retryable(err error) error

Retryable defines an error as retryable.

Types

type Client

type Client struct {
	*kithttp.Client
}

Client is a wrapper above the go-kit http client. It maps HTTP errors to Go errors. As the mapped error implements StatusCode, the returned status code will also be used as the status code returned by a go-kit HTTP endpoint. When using the backoff middleware, only 429 & 5XX errors trigger a retry.

func NewClient

func NewClient(
	method string,
	tgt *url.URL,
	enc kithttp.EncodeRequestFunc,
	dec kithttp.DecodeResponseFunc,
	options ...kithttp.ClientOption,
) *Client

NewClient creates a kitty client.

func NewClientWithError added in v0.0.8

func NewClientWithError(
	method string,
	tgt *url.URL,
	enc kithttp.EncodeRequestFunc,
	dec kithttp.DecodeResponseFunc,
	options ...kithttp.ClientOption,
) *Client

NewClientWithError creates a kitty client that doesn't deal with HTTP errors. and let you do it while you decode.

type Config

type Config struct {
	// LivenessCheckPath is the path of the health handler (default: "/alivez").
	LivenessCheckPath string
	// ReadinessCheckPath is the path of the readiness handler (default: "/readyz").
	ReadinessCheckPath string
	// HTTPPort is the port the server will listen on (default: 8080).
	HTTPPort int
	// EnablePProf enables pprof urls (default: false).
	EnablePProf bool
	// EncodeResponse defines the default response encoder for all endpoints (by default: EncodeJSONResponse). It can be overriden for a specific endpoint.
	EncodeResponse kithttp.EncodeResponseFunc
}

Config holds configuration info for kitty.HTTPTransport.

type HTTPEndpointOption

type HTTPEndpointOption func(*httpendpoint) *httpendpoint

HTTPEndpointOption is an option for an HTTP endpoint

func Decoder

Decoder defines the request decoder for a HTTP endpoint. If none is provided, NopRequestDecoder is used.

func Encoder

Encoder defines the response encoder for a HTTP endpoint. If none is provided, EncodeJSONResponse is used.

func ServerOptions

func ServerOptions(opts ...kithttp.ServerOption) HTTPEndpointOption

ServerOptions defines a liste of go-kit ServerOption to be used by a HTTP endpoint.

type HTTPTransport added in v0.0.4

type HTTPTransport struct {
	// contains filtered or unexported fields
}

HTTPTransport defines a HTTP transport for a kitty Server.

func NewHTTPTransport added in v0.0.4

func NewHTTPTransport(cfg Config) *HTTPTransport

NewHTTPTransport creates a new HTTP transport, based on the specified config.

func (*HTTPTransport) Endpoint added in v0.0.4

func (t *HTTPTransport) Endpoint(method, path string, ep endpoint.Endpoint, opts ...HTTPEndpointOption) *HTTPTransport

Endpoint registers an endpoint to a kitty.HTTPTransport. Unless specified, NopRequestDecoder will decode the request (and do nothing), and EncodeJSONResponse will encode the response.

func (*HTTPTransport) HTTPMiddlewares added in v0.0.4

func (t *HTTPTransport) HTTPMiddlewares(m ...func(http.Handler) http.Handler) *HTTPTransport

HTTPMiddlewares defines the list of HTTP middlewares to be added to all HTTP handlers.

func (*HTTPTransport) Liveness added in v0.0.4

func (t *HTTPTransport) Liveness(h http.HandlerFunc) *HTTPTransport

Liveness defines the liveness handler.

func (*HTTPTransport) LogKeys added in v0.0.4

func (t *HTTPTransport) LogKeys() map[string]interface{}

LogKeys returns the list of name key to context key mappings

func (*HTTPTransport) Options added in v0.0.4

func (t *HTTPTransport) Options(opts ...kithttp.ServerOption) *HTTPTransport

Options defines the list of go-kit http.ServerOption to be added to all endpoints.

func (*HTTPTransport) Readiness added in v0.0.4

func (t *HTTPTransport) Readiness(h http.HandlerFunc) *HTTPTransport

Readiness defines the readiness handler.

func (*HTTPTransport) RegisterEndpoints added in v0.0.4

func (t *HTTPTransport) RegisterEndpoints(m endpoint.Middleware) error

RegisterEndpoints registers all configured endpoints, wraps them with the m middleware.

func (*HTTPTransport) Router added in v0.0.4

func (t *HTTPTransport) Router(r Router, opts ...RouterOption) *HTTPTransport

Router defines the router to use in a server.

func (*HTTPTransport) ServeHTTP added in v0.0.4

func (t *HTTPTransport) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler.

func (*HTTPTransport) Shutdown added in v0.0.4

func (t *HTTPTransport) Shutdown(ctx context.Context) error

Shutdown shutdowns the HTTP server

func (*HTTPTransport) Start added in v0.0.4

func (t *HTTPTransport) Start(ctx context.Context) error

Start starts the HTTP server.

type LogOption added in v0.0.3

type LogOption int

LogOption is a LogEndpoint middleware option.

const (
	// LogRequest logs the request.
	LogRequest LogOption = iota
	// LogResponse logs the response.
	LogResponse
	// LogErrors logs the request in case of an error.
	LogErrors
)

type Retryabler

type Retryabler interface {
	Retryable() bool
	Cause() error
}

Retryabler defines an error that may be temporary. A function returning a retryable error may be executed again.

type Router

type Router interface {
	// Handle registers a handler to the router.
	Handle(method string, path string, handler http.Handler)
	// SetNotFoundHandler will sets the NotFound handler.
	SetNotFoundHandler(handler http.Handler)
	// ServeHTTP implements http.Handler.
	ServeHTTP(w http.ResponseWriter, r *http.Request)
}

Router is an interface for router implementations.

func StdlibRouter

func StdlibRouter() Router

StdlibRouter returns a Router based on the stdlib http package.

type RouterOption

type RouterOption func(Router) Router

RouterOption sets optional Router options.

func NotFoundHandler

func NotFoundHandler(h http.Handler) RouterOption

NotFoundHandler will set the not found handler of the router.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server defines a kitty server.

Example
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/objenious/kitty"
	"github.com/objenious/kitty/gorilla"
)

func main() {
	type fooRequest struct{ Name string }
	type fooResponse struct{ Message string }

	foo := func(ctx context.Context, request interface{}) (interface{}, error) {
		fr := request.(fooRequest)
		return fooResponse{Message: fmt.Sprintf("Good morning %s !", fr.Name)}, nil
	}

	decodeFooRequest := func(ctx context.Context, r *http.Request) (interface{}, error) {
		var request fooRequest
		if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
			return nil, err
		}
		return request, nil
	}
	t := kitty.NewHTTPTransport(kitty.Config{HTTPPort: 8081}).
		Router(gorilla.Router()).
		Endpoint("POST", "/foo", foo, kitty.Decoder(decodeFooRequest))
	kitty.NewServer(t).Run(context.Background())
}
Output:

func NewServer

func NewServer(t ...Transport) *Server

NewServer creates a kitty server.

func (*Server) LogContext

func (s *Server) LogContext(keys ...string) *Server

LogContext defines the list of keys to add to all log lines. Keys may vary depending on transport. Available keys for the http transport are : http-method, http-uri, http-path, http-proto, http-requesthost, http-remote-addr, http-x-forwarded-for, http-x-forwarded-proto, http-user-agent and http-x-request-id.

func (*Server) Logger

func (s *Server) Logger(l log.Logger) *Server

Logger sets the logger.

func (*Server) Middlewares

func (s *Server) Middlewares(m ...endpoint.Middleware) *Server

Middlewares defines the list of endpoint middlewares to be added to all endpoints.

func (*Server) Run

func (s *Server) Run(ctx context.Context) error

Run starts the server.

func (*Server) Shutdown

func (s *Server) Shutdown(fns ...func()) *Server

Shutdown registers functions to be called when the server is stopped.

type Transport added in v0.0.4

type Transport interface {
	// RegisterEndpoints registers all endpoints. Endpoints needs to be wrapped with the specified middleware.
	RegisterEndpoints(m endpoint.Middleware) error
	// LogKeys returns the list of name key (as configured in Server.LogContext) to context key (as set in context.WithValue) mappings
	// for logging. Transports are responsible for injecting the corresponding values in the context.
	LogKeys() map[string]interface{}
	// Start starts the transport.
	Start(ctx context.Context) error
	// Shutdown shutdowns the transport.
	Shutdown(ctx context.Context) error
}

Transport is the interface all transports must implement.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL