services

package module
v0.0.0-...-b7cf387 Latest Latest
Warning

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

Go to latest
Published: Dec 31, 2022 License: MIT Imports: 7 Imported by: 2

README

Go Report Card codecov

DISCLAIMER: This is a work in progress and is not being used in production yet. Breaking changes might be introduced without previous warning.

go-services

go-services is a go library that implement resources and servers and let you start and stop them gracefully.

Usage

package main

import (
	"github.com/jamillosantos/go-services"

	"yourproject/internal/resources"
	"yourproject/internal/servers"
)

func main() {
	ctx := context.Background()

	runner := services.NewRunner()
	defer runner.Finish(ctx)

	err := runner.Run(
		ctx,
		resources.Pg,
		resources.Kafka,
		resources.Redis,
	)
	if err != nil {
		panic(err)
	}

	fmt.Println("[hit Ctrl+C] to finish ...")
	err := runner.Run(
		ctx,
		servers.Grpc,
		servers.PrometheusMetrics,
	)
	if err != nil {
		panic(err)
	}
}

Ready to use

TODO: Add list of Resource and Server implementations.

Implementing Resource

Resources are dependencies, usually external, needs to be initialized before the main processing of the service. They are started in sequence and should never block when starting. When the service stops, resources will be stoped on the reverse order they were started. Examples: Database connections, Amazon SQS, etc. Anything that needs to be started when the service starts, and stopped when the services is shutting down.

type Resource interface {
	Name() string
	Start(ctx context.Context) error
	Stop(ctx context.Context) error
}

It has a pretty straight forward implementation. The only advise is that Start can block until initialized, and after that it should release the "thread".

If any Resource fails to start, the ResourceStarter will stop all previous ones.

Implementing Server

Servers are dependencies that block the flow of the service. They are initialized in parallel and will block until the service shuts down. Examples: HTTP servers, gRPC servers, consumers.

type Server interface {
	Name() string
	Listen(ctx context.Context) error
	Close(ctx context.Context) error
}

Documentation

Index

Constants

View Source
const (
	FSFinishing finishState = "finishing"
	FSFinished  finishState = "finished"
)

Variables

View Source
var (
	// DefaultSignals is the list of signals that the Runner will listen if no listener is specified.
	DefaultSignals = []os.Signal{os.Interrupt}
)
View Source
var (
	ErrInvalidServiceType = errors.New("invalid service type")
)

Functions

This section is empty.

Types

type Configurable

type Configurable interface {
	// Load will load the configuration
	Load(ctx context.Context) error
}

Configurable describes a service that should be loaded before started.

This method will be used direct by `Runner`. Before starting a service, `Runner` will call `Load` (if available) before continuing starting the service. If it fails, `Runner.Run` will fail. Otherwise, the starting process will continue normally.

type MultiErrors

type MultiErrors []error

func (MultiErrors) Error

func (errs MultiErrors) Error() string

type Observer

type Observer interface {
	BeforeStart(context.Context, Service)
	AfterStart(context.Context, Service, error)
	BeforeStop(context.Context, Service)
	AfterStop(context.Context, Service, error)
	BeforeLoad(context.Context, Configurable)
	AfterLoad(context.Context, Configurable, error)

	SignalReceived(os.Signal)
}

Observer will be called Before and After some actions by a `Runner`.

type Reporter

type Reporter interface {
	BeforeStart(context.Context, Service)
	AfterStart(context.Context, Service, error)
	BeforeStop(context.Context, Service)
	AfterStop(context.Context, Service, error)
	BeforeLoad(context.Context, Configurable)
	AfterLoad(context.Context, Configurable, error)

	SignalReceived(os.Signal)
}

Reporter will be called Before and After some actions by a `Runner`. Deprecated: Use the Observer interface instead.

type Resource

type Resource interface {
	Service

	// Start will initialize the resource making it ready for use. This method should block until the resource is ready.
	// It should be implemented on a way so that when Stop is called, this should be cancelled. If that is not possible,
	// Stop should wait until Start finish before proceeding.
	//
	// If the service is successfully started, `nil` should be returned. Otherwise, an error must be returned.
	Start(ctx context.Context) error

	// Stop will release this Resource. If it is called while Start still running, Stop should cancel the Start, or then
	// wait for it to be finished before proceeding.
	//
	// For most implementations it will be blocking and should return only when the service finishes stopping. This is
	// important because the Runner relies on it to proceed to the next Resource.
	//
	// If the service is successfully stopped, `nil` should be returned. Otherwise, an error must be returned.
	Stop(ctx context.Context) error
}

Resource is the interface that must be implemented for resourceServices that its start is NOT cancellable.

type ResourceServiceRetrier

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

ResourceServiceRetrier wraps a `Service` in order to provide functionality for retrying in case of its starting process fails.

func (*ResourceServiceRetrier) Name

func (retrier *ResourceServiceRetrier) Name() string

Name will return a human identifiable name for this service. Ex: Postgresql Connection.

func (*ResourceServiceRetrier) Start

func (retrier *ResourceServiceRetrier) Start(ctx context.Context) error

Start implements the logic of starting a service. If it fails, it should use the configuration to retry.

func (*ResourceServiceRetrier) Stop

func (retrier *ResourceServiceRetrier) Stop(ctx context.Context) error

Stop will stop this service.

For most implementations it will be blocking and should return only when the service finishes stopping.

If the service is successfully stopped, `nil` should be returned. Otherwise, an error must be returned.

type RetrierBuilder

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

RetrierBuilder is the helper for building `ResourceServiceRetrier`.

func Retrier

func Retrier() *RetrierBuilder

Retrier returns a new `RetrierBuilder` instance.

func (*RetrierBuilder) Backoff

func (builder *RetrierBuilder) Backoff(value backoff.BackOff) *RetrierBuilder

Backoff set the timeout for the `Retrier`.

func (*RetrierBuilder) Build

func (builder *RetrierBuilder) Build(service Resource) Resource

Build creates a new `ResourceServiceRetrier` with

func (*RetrierBuilder) Reporter

func (builder *RetrierBuilder) Reporter(value RetrierReporter) *RetrierBuilder

Reporter set the reporter for the `Retrier`.

type RetrierReporter

type RetrierReporter interface {
	Reporter
	BeforeRetry(context.Context, Service, int)
}

type Runner

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

func NewRunner

func NewRunner(opts ...StarterOption) *Runner

NewRunner creates a new instance of Runner.

If a listener is not defined, it will create one based on DefaultSignals.

func (*Runner) Finish

func (r *Runner) Finish(ctx context.Context) (errResult error)

Finish will go through all started resourceServices, in the opposite order they were started, stopping one by one. If any, failure is detected, the function will stop leaving some started resourceServices.

func (*Runner) Run

func (r *Runner) Run(ctx context.Context, services ...Service) (errResult error)

Run goes through all given Service instances trying to start them. This function only supports Resource or Server instances (subset of Service). Then, it goes through all of them starting each one.

Resource instances are initialized by calling Resource.Start, respecting the given order, only one at a time. If only Resource instances are passed, this function will not block and Run can be called many times (not thread-safe).

Server instances are initialized by invoking a new goroutine that calls the Server.Listen. So, the order is not be guaranteed and all Server starts at once. Then, Run blocks until all server are closed and it can happen in two cases: when a specified os.Signal is received (check WithListenerBuilder or WithSignals for more information) or when the given ctx is cancelled. Either cases the Run will gracefully stop all Server instances that were initialized (by calling Server.Close).

Important: Resource instances will not be stopped when the a os.Signal is received or the ctx is cancelled. For that, you should call Runner.Finish.

If you need to cancel the Run method. You can use the context.WithCancel applied to the given ctx.

Whenever this function exists, all given Server instances will be closed by using Server.Close. Then, it will wait until the Server.Listen finished.

type Server

type Server interface {
	Service

	// Listen will start the server and should never block.
	//
	// If the services is already listining, this should return an error ErrAlreadyListening.
	Listen(ctx context.Context) error

	// Close will stop this service. It should block until all resources are released and the server is properly
	// shutdown.
	//
	// If the services has not started, or is already stopped, this should do nothing and just return nil.
	Close(ctx context.Context) error
}

Server is the interface that must be implemented for resourceServices that its start is NOT cancellable.

type Service

type Service interface {
	// Name will return a human identifiable name for this service. Ex: Postgresql Connection.
	Name() string
}

Service is the abstraction of what minimum signature a service must have.

type StarterOption

type StarterOption = func(*Runner)

func WithListenerBuilder

func WithListenerBuilder(builder func() signals.Listener) StarterOption

WithListenerBuilder is a StarterOption that will set the signal listener instance of a Runner.

func WithObserver

func WithObserver(observer Observer) StarterOption

WithObserver is a StarterOption that will add an observer instance on the list.

func WithReporter

func WithReporter(reporter Observer) StarterOption

WithReporter is a StarterOption that will set the signal listener instance of a Runner.

func WithSignals

func WithSignals(ss ...os.Signal) StarterOption

WithSignals is a StarterOption that will setup a listener builder that create a listener with the given signals.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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