patron

package module
v0.3.2 Latest Latest
Warning

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

Go to latest
Published: Jun 24, 2018 License: Apache-2.0 Imports: 16 Imported by: 0

README

patron Build Status codecov Go Report Card GoDoc

Patron is a framework for creating microservices.

Patron is french for template or pattern, but it means also boss which we found out later (no pun intended).

The entry point of the framework is the Service. The Service uses Components to handle the processing of sync and async requests. The Service starts by default a HTTP Component which hosts the debug, health and metric endpoints. Any other endpoints will be added to the default HTTP Component as Routes. The service set's up by default logging with zerolog, tracing and metrics with jaeger.

Patron provides abstractions for the following functionality of the framework:

  • service
  • components and processors
    • asynchronous message processing (RabbitMQ, Kafka)
    • synchronous processing (HTTP)
  • metrics and tracing
  • logging
  • configuration

Service

The Service has the role of glueing all of the above together, which are:

  • setting up logging
  • setting up termination by user
  • starting and stopping components
  • handling component errors

The service has some default settings tha can be changed via environment variables:

  • Service HTTP port, which set's up the default HTTP components port to 50000 which can be changed via the PATRON_HTTP_DEFAULT_PORT
  • Log level, which set's up zerolog with INFO as log level which can be changed via the PATRON_LOG_LEVEL
  • Tracing, which set's up jaeger tracing with
    • agent address 0.0.0.0:6831, which can be changed via PATRON_JAEGER_AGENT
    • sampler type probabilistic, which can be changed via PATRON_JAEGER_SAMPLER_TYPE
    • sampler param 0.1, which can be changed via PATRON_JAEGER_SAMPLER_PARAM
Component

A Component is a interface that exposes the following API:

type Component interface {
  Run(ctx context.Context) error
  Shutdown(ctx context.Context) error
}

The above API gives the Service the control over a component in order to start and stop it gracefully. The framework divides the components in 2 categories:

  • synchronous, which are components that follow the request/response pattern and
  • asynchronous, which consume messages from a source but don't respond anything back

The following component implementations are available:

  • HTTP (sync)
  • RabbitMQ (async)
  • Kafka (async)

Adding to the above list is as easy as implementing a Component and a Processor for that component.

Example

Setting up a new service with a HTTP Component is as easy as the following code:

  // Set up HTTP routes
  routes := make([]sync_http.Route, 0)
  routes = append(routes, sync_http.NewRoute("/", http.MethodGet, process, true))
  
  srv, err := patron.New("test", patron.Routes(routes))
  if err != nil {
    log.Fatalf("failed to create service %v", err)
  }

  err = srv.Run()
  if err != nil {
    log.Fatalf("failed to create service %v", err)
  }

The above is pretty much self-explanatory.

Processors

Synchronous

The implementation of the processor is responsible to create a Request by providing everything that is needed (Headers, Fields, decoder, raw io.Reader) pass it to the implementation by invoking the Process method and handle the Response or the error returned by the processor.

The sync processor package contains only a interface definition of the processor along the models needed:

type Processor interface {
  Process(context.Context, *Request) (*Response, error)
}

The Request model contains the following properties (which are provided when calling the "constructor" NewRequest)

  • Headers, which may contains any headers associated with the request
  • Fields, which may contain any fields associated with the request
  • Raw, the raw request data (if any) in the form of a io.Reader
  • decode, which is a function of type encoding.Decode that decodes the raw reader

A exported function exists for decoding the raw io.Reader in the form of

Decode(v interface{}) error

The Response model contains the following properties (which are provided when calling the "constructor" NewResponse)

  • Payload, which may hold a struct of type interface{}
Asynchronous

The implementation of the async processor follows exactly the same principle as the sync processor. The main difference is that:

  • The Request is the Message and contains only data as []byte
  • There is no Response, so the processor may return a error
type Processor interface {
  Process(context.Context, *Message) error
}

Everything else is exactly the same.

Metrics and Tracing

Tracing and metrics are provided by jaeger's implementation of the OpenTracing project. Every component has been integrated with the above library and produces traces and metrics. Metrics are provided with the default HTTP component at the /metrics route for Prometheus to scrape. Tracing will be send to a jaeger agent which can be setup though environment variables mentioned in the config section.

Logging

The log package is designed to be a leveled logger with field support.

The log package defines two interfaces (Logger and Factory) that have to be implemented in order to set up the logging in this framework. After implementing the two interfaces you can setup logging by doing the following:

  // instantiate the implemented factory f
  err := log.Setup(f)
  // handle error

The implementations should support following log levels:

  • Debug, which should log the message with debug level
  • Info, which should log the message with info level
  • Warn, which should log the message with warn level
  • Error, which should log the message with error level
  • Panic, which should log the message with panic level and panics
  • Fatal, which should log the message with fatal level and terminates the application

The first four (Debug, Info, Warn and Error) give the opportunity to differentiate the messages by severity. The last two (Panic and Fatal) do the same and do additional actions (panic and termination).

The package supports fields, which are logged along with the message, to augment the information further to ease querying in the log management system.

The following implementations are provided as sub-package:

  • zerolog, which supports the excellent zerolog library
Logger

The logger interface defines the actual logger.

type Logger interface {
  Level() Level
  Fields() map[string]interface{}
  Fatal(...interface{})
  Fatalf(string, ...interface{})
  Panic(...interface{})
  Panicf(string, ...interface{})
  Error(...interface{})
  Errorf(string, ...interface{})
  Warn(...interface{})
  Warnf(string, ...interface{})
  Info(...interface{})
  Infof(string, ...interface{})
  Debug(...interface{})
  Debugf(string, ...interface{})
}

In order to be consistent with the design the implementation of the Fatal(f) have to terminate the application with an error and the Panic(f) need to panic.

Factory

The factory interface defines a factory for creating a logger.

type Factory interface {
  Create(map[string]interface{}) Logger
  CreateSub(Logger, map[string]interface{}) Logger
}

Two methods are supported:

  • Create, which creates a logger with the specified fields (or nil)
  • CreateSub, which creates a sub-logger that accepts a logger and fields and creates a sub-logger with the fields merged into the new one.

Config

The config package defines a interface that has to be implemented in order to be used inside the application.

type Config interface {
  Set(key string, value interface{}) error
  Get(key string) (interface{}, error)
  GetBool(key string) (bool, error)
  GetInt64(key string) (int64, error)
  GetString(key string) (string, error)
  GetFloat64(key string) (float64, error)
}

After implementing the interface a instance has to be provided to the Setup method of the package in order to be used directly from the package eg config.GetBool().

The following implementations are provided as sub-packages:

  • env, support for env files and env vars
env

The env package supports getting env vars from the system. It allows further to provide a file that contain env vars, separated by a equal sign =, which are then set up on the environment. In order to setup config just do the following:

c,err := env.New({reader to the config file})
// error checking
config.Setup(c)

Documentation

Overview

Package patron framework

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Component added in v0.2.0

type Component interface {
	Run(ctx context.Context) error
	Shutdown(ctx context.Context) error
}

Component interface for implementing components.

type Option added in v0.2.0

type Option func(*Service) error

Option defines a option for the HTTP service.

func Components added in v0.3.2

func Components(cc []Component) Option

Components option for adding additional components to the service.

func Routes added in v0.3.2

func Routes(rr []http.Route) Option

Routes option for adding routes to the default HTTP component.

type Service

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

Service definition.

func New

func New(name string, oo ...Option) (*Service, error)

New creates a new service

func (*Service) Run

func (s *Service) Run() error

Run starts up all service components and monitors for errors.

func (*Service) Shutdown

func (s *Service) Shutdown() error

Shutdown performs a shutdown on all components with the setup timeout.

Directories

Path Synopsis
env
log

Jump to

Keyboard shortcuts

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