nserve

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2021 License: MIT Imports: 4 Imported by: 0

README

nserve - server startup/shutdown in an nject world

GoDoc Coverage

Install:

go get github.com/muir/nject

This package provides server startup and shutdown wrappers that can be used with libraries and servers that are stated with nject.

How to structure your application

Libraries become injected dependencies. They can in turn have other libraries as dependencies of them. Since only the depenencies that are required are actaully injected, the easiest thing is to have a master list of all your libraries and provide that to all your apps.

Let's call that master list allLibraries.

app, err := nserve.CreateApp("myApp", allLibrariesSequence, createAppFunction)
err = app.Do(nserve.Start)
err = app.Do(nserve.Stop)
Hooks

Libaries and appliations can register callbacks on a per-hook basis. Two hooks are pre-provided by other hooks can be created.

Hook invocation can be limited by a timeout. If the hook does not complete in that amount of time, the hook will return error and continue processing in the background.

The Start, Stop, and Shutdown hooks are pre-defined:

var Shutdown = NewHook("shutdown", ReverseOrder)
var Stop = NewHook("stop", ReverseOrder).OnError(Shutdown).ContinuePastError(true)
var Start = NewHook("start", ForwardOrder).OnError(Stop)

Hooks can be invoked in registration order (ForwardOrder) or in reverse registration order ReverseOrder.

If there is an OnError modifier for the hook registration, then that hook gets invoked if there is an error returned when the hook is running.

Libraries and applications can register callbacks for hooks by taking an *nserve.App as as an input parameter and then using that to register callbacks:

func CreateMyLibrary(app *nserve.App) *MyLibrary {
	lib := &MyLibrary{ ... }
	app.On(Start, lib.Start)
	return lib
}
func (lib *MyLibrary) Start(app *nserve.App) {
	app.On(Stop, lib.Stop)
}

The callback function can be any nject injection chain. If it ends with a function that can return error, then any such error will be become the error return from app.Do and if there is an OnError handler for that hook, that handler will be invoked.

Documentation

Overview

Package nserve helps with server startup and shutdown by by allowing libraries too register themselves with hooks that run at startup and shutdown.

Example

Example shows the injection, startup, and shutdown of an app with two libraries

package main

import (
	"fmt"

	"github.com/muir/nject/nserve"
	"github.com/pkg/errors"
)

type (
	L1 struct{}
	L2 struct{}
	L3 struct{}
)

func NewL1(app *nserve.App) *L1 {
	fmt.Println("L1 created")
	app.On(nserve.Start, func(app *nserve.App) {
		app.On(nserve.Stop, func() error {
			fmt.Println("L1 stopped")
			return fmt.Errorf("L1 stop error")
		})
		fmt.Println("L1 started")
	})
	return &L1{}
}

func NewL2(app *nserve.App, _ *L1) *L2 {
	fmt.Println("L2 created")
	app.On(nserve.Start, func(app *nserve.App) error {
		app.On(nserve.Stop, func() error {
			fmt.Println("L2 stopped")
			return fmt.Errorf("L2 stop error")
		})
		fmt.Println("L2 started")
		// Note: Library2 start will return error
		return fmt.Errorf("L2 start error")
	})
	return &L2{}
}

func NewL3(_ *L2, app *nserve.App) *L3 {
	fmt.Println("L3 created")
	app.On(nserve.Start, func(app *nserve.App) {
		fmt.Println("L3 started")
	})
	return &L3{}
}

func ErrorCombiner(e1, e2 error) error {
	return errors.New(e1.Error() + "; " + e2.Error())
}

// Example shows the injection, startup, and shutdown of an app with two libraries
func main() {
	nserve.Start.SetErrorCombiner(ErrorCombiner)
	nserve.Stop.SetErrorCombiner(ErrorCombiner)
	nserve.Shutdown.SetErrorCombiner(ErrorCombiner)
	app, err := nserve.CreateApp("myApp", NewL1, NewL2, NewL3, func(_ *L1, _ *L2, _ *L3, app *nserve.App) {
		fmt.Println("App created")
	})
	fmt.Println("create error:", err)
	err = app.Do(nserve.Start)
	fmt.Println("do start error:", err)
}
Output:

L1 created
L2 created
L3 created
App created
create error: <nil>
L1 started
L2 started
L1 stopped
L2 stopped
do start error: L2 start error; L1 stop error; L2 stop error

Index

Examples

Constants

View Source
const (
	// ForwardOrder is used to indicate that the items
	// registered for a hook will be invoked in the order
	// that they were registered.
	ForwardOrder hookOrder = "forward"
	// ReverseOrder is used to indicate that the items
	// registered for a hook will be invoked opposite to the order
	// that they were registered.
	ReverseOrder = "forward"
)

Variables

View Source
var Shutdown = NewHook("shutdown", ReverseOrder)

Shutdown is a reverse-order hook meant to be used for forced shutdowns. If Stop encounters an error, then Shutdown will also be called.

View Source
var Start = NewHook("start", ForwardOrder).OnError(Stop)

Start is a forward-order hook for starting services. If it encounters an error, it will invoke Stop on whatever was started.

View Source
var Stop = NewHook("stop", ReverseOrder).OnError(Shutdown).ContinuePastError(true)

Stop is a reverse-order hook meant to be used when stopping. If an error is encountered, Shutdown will also be used.

Functions

This section is empty.

Types

type App

type App struct {
	Hooks map[hookId][]nject.Provider
	// contains filtered or unexported fields
}

App provides hooks to start and stop libraries that are used by an app. It expected that an App corresponds to a service and that libraries that the service uses need to be started & stopped.

func CreateApp

func CreateApp(name string, providers ...interface{}) (*App, error)

CreateApp will use nject.Run() to invoke the providers that make up the service represented by the app.

func (*App) Do

func (app *App) Do(h *Hook) error

Do invokes the callbacks for a hook. It returns only the first error reported unless the hook provides an error combiner that preserves the other errors.

func (*App) On

func (app *App) On(h *Hook, providers ...interface{})

On registers a callback to be invoked on hook invocation. This can be used during callbacks, for example a start callback, can register a stop callback. Each call to On() adds one nject provider chain. By default, only the last function will be called and nject annotations can be used to control the behavior.

type Hook

type Hook struct {
	Id hookId

	Name          string
	Order         hookOrder
	InvokeOnError []*Hook
	ContinuePast  bool
	ErrorCombiner func(first, second error) error
	Providers     []interface{}
	// contains filtered or unexported fields
}

Hook is the handle/name for a list of callbacks to invoke.

func NewHook

func NewHook(name string, order hookOrder) *Hook

NewHook creates a new category of callbacks.

func (*Hook) ContinuePastError

func (h *Hook) ContinuePastError(b bool) *Hook

ContinuePastError sets if callbacks should continue to be invoked if there has already been an error. ContinuePastError is thread-safe.

func (*Hook) Copy

func (h *Hook) Copy() *Hook

Copy makes a deep copy of a hook and the new hook gets a new Id. Copy is thread-safe.

func (*Hook) OnError

func (h *Hook) OnError(e *Hook) *Hook

OnError adds to the set of hooks to invoke when this hook is thows an error. Call with nil to clear the set of hooks to invoke. OnError is thread-safe.

func (*Hook) SetErrorCombiner

func (h *Hook) SetErrorCombiner(f func(first, second error) error) *Hook

SetErrorCombiner sets a function to combine two errors into one when there is more than one error to return from a invoking all the callbacks SetErrorCombiner is thread-safe.

func (*Hook) String

func (h *Hook) String() string

String is not thread-safe with respect to reaching into a hook and changing it's Name. Don't do that.

Jump to

Keyboard shortcuts

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