endure

package module
v2.4.4 Latest Latest
Warning

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

Go to latest
Published: Mar 27, 2024 License: MIT Imports: 19 Imported by: 5

README

Endure

RoadRunner is an open-source (MIT licensed) high-performance PHP application server, load balancer, and process manager. It supports running as a service with the ability to extend its functionality on a per-project basis.

RoadRunner includes PSR-7/PSR-17 compatible HTTP and HTTP/2 server and can be used to replace classic Nginx+FPM setup with much greater performance and flexibility.

Official Website | Documentation | Forum | Release schedule

Endure is an open-source (MIT licensed) plugin container with IoC (Inversion of Control).

Features

  • Supports interfaces (see examples)
  • Uses a graph to topologically sort, run, stop, and restart dependent plugins
  • Supports easy addition of Middleware plugins
  • Error reporting

Installation

go get -u github.com/roadrunner-server/endure/v2
Why?

Imagine you have an application in which you want to implement a plugin system. These plugins can depend on each other (via interfaces or directly). For example, we have 3 plugins: HTTP (to communicate with the world), DB (to save the world), and logger (to see the progress). In this particular case, we can't start HTTP before we start all other parts. Also, we have to initialize the logger first, because all parts of our system need the logger. All you need to do in Endure is to pass the HTTP, DB, and Logger structs to Endure and implement the Endure interface. So, the dependency graph will be the following:

Dependency Graph

First, we initialize the endure container:

import (
    "log/slog"
)

func main() {
    container := endure.New(slog.LevelDebug, endure.Visualize())
}

Let's take a look at the endure.New() function:

  1. The first argument is the standard golang logger log level.
  2. The next arguments are optional and can be set using Options. For example, endure.Visualize() will show you a dot-compatible graph in the console. Then we need to pass our structures as references to the RegisterAll or Register function.
err = container.RegisterAll(
    &httpPlugin{},
    &DBPlugin{},
    &LoggerPlugin{},
	)
    if err != nil {
        panic(err)
    }

The order of plugins in the RegisterAll function does not matter. Next, we need to initialize and run our container:

err := container.Init()
    if err != nil {
        panic(err)
}
errCh, err := container.Serve()
    if err != nil {
    	panic(err)
}

errCh is the channel with errors from all Vertices. You can identify the vertex by vertexID, which is presented in the errCh struct. Then just process the events from the errCh:

for {
    select {
        case e := <-errCh:
            println(e.Error.Err.Error()) // just print the error, but actually error processing could be there
            er := container.Stop()
            if er != nil {
                panic(er)
            }
        return
    }
}

The start will proceed in topological order (Logger -> DB -> HTTP), and the stop in reverse-topological order automatically.

Endure main interface
package sample

import (
	"context"
	
	"github.com/roadrunner-server/endure/v2/dep"
)

type (
   // This is the main Endure service interface which may be implemented to Start (Serve) and Stop plugin (OPTIONAL)
   Service interface {
      // Serve
      Serve() chan error
      // Stop with context, if you reach the timeout, endure will force the exit via context deadline
      Stop(context.Context) error
      // Named return plugin's name
      Named() string
   }

   // Provider declares the ability to provide dependencies to other plugins (OPTIONAL)
   Provider interface {
      Provides() []*dep.In
   }

   // Collector declares the ability to accept the plugins which match the provided method signature (OPTIONAL)
   Collector interface {
      Collects() []*dep.Out
   }
)

// Init is mandatory to implement
type Plugin struct{}

func (p *Plugin) Init( /* deps here */) error {
   return nil
}

Order is the following:

  1. Init() error - is mandatory to implement. For your structure (which you pass to Endure), you should have this method as the method of the struct (go func (p *Plugin) Init() error {}). It can accept as a parameter any passed to the Endure structure (see samples) or interface (with limitations).
  2. Service - is optional to implement. It has 2 methods: Serve which should run the plugin and return an initialized golang channel with errors, and Stop to shut down the plugin. The Stop and Serve should not block the execution.
  3. Provider - is optional to implement. It is used to provide some dependency if you need to extend your struct without deep modification.
  4. Collector - is optional to implement. It is used to mark a structure (vertex) as some struct dependency. It can accept interfaces that implement a caller.
  5. Named - is mandatory to implement. This is a special kind of interface that provides the name of the struct (plugin, vertex) to the caller. It is useful in the logger (for example) to know the user-friendly plugin name.

Available options:

  1. Visualize: Graph visualization option via graphviz. The Graphviz diagram can be shown via stdout.
  2. GracefulShutdownTimeout: time.Duration. How long to wait for a vertex (plugin) to stop.

The fully operational example is located in the examples folder.

Documentation

Index

Constants

View Source
const (
	// InitMethodName is the function fn for the reflection
	InitMethodName = "Init"
	// ServeMethodName is the function fn for the Serve
	ServeMethodName = "Serve"
	// StopMethodName is the function fn for the reflection to Stop the service
	StopMethodName = "Stop"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Collector

type Collector interface {
	// Collects search for the plugins which implements given interfaces in the args
	Collects() []*dep.In
}

Collector declares the ability to accept the plugins which match the provided method signature.

type Container

type Container interface {
	// Serve used to Start the plugin in topological order
	Serve() (<-chan *Result, error)
	// Stop stops the plugins in rev-topological order
	Stop() error
	// Register registers one plugin in container
	Register(service any) error
	// Plugins method is responsible for returning an all registered plugins
	Plugins() string
	// RegisterAll register set of comma separated plugins in container
	RegisterAll(service ...any) error
	// Init initializes all plugins (calling Init function), calculate vertices, invoke Collects and Provided functions if exist
	Init() error
}

Container - Internal container interface

type Endure

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

Endure struct represent main endure repr

func New

func New(level slog.Leveler, options ...Options) *Endure

New returns empty endure container

func (*Endure) Init

func (e *Endure) Init() error

func (*Endure) Plugins

func (e *Endure) Plugins() []string

func (*Endure) Register

func (e *Endure) Register(vertex any) error

Register registers the dependencies in the Endure graph without invoking any methods

func (*Endure) RegisterAll

func (e *Endure) RegisterAll(plugins ...any) error

RegisterAll is the helper for the register to register more than one structure in the endure

func (*Endure) Serve

func (e *Endure) Serve() (<-chan *Result, error)

Serve used to start serving vertices Do not change this method fn, sync with constants in the beginning of this file

func (*Endure) Stop

func (e *Endure) Stop() error

Stop used to shutdown the Endure Do not change this method fn, sync with constants in the beginning of this file

func (*Endure) Visualize

func (e *Endure) Visualize(vertices []*graph.Vertex) error

Visualize visualizes the graph based on provided output value

type Named

type Named interface {
	// Name return user-friendly name of the plugin
	Name() string
}

Named -> Name of the service

type Options

type Options func(endure *Endure)

Options is the endure options

func EnableProfiler

func EnableProfiler() Options

func GracefulShutdownTimeout

func GracefulShutdownTimeout(to time.Duration) Options

GracefulShutdownTimeout sets the timeout to kill the vertices is one or more of them are frozen

func LogHandler added in v2.3.0

func LogHandler(handler slog.Handler) Options

LogHandler defines the logger handler to create the slog.Logger

For example:

container = endure.New(slog.LevelInfo, LogHandler(slog.NewTextHandler(
 os.Stderr, &slog.HandlerOptions{Level: slog.LevelInfo})))

func Visualize

func Visualize() Options

type Provider

type Provider interface {
	// Provides function return set of functions which provided dependencies to other plugins
	Provides() []*dep.Out
}

Provider declares the ability to provide service edges of declared types.

type Result

type Result struct {
	Error    error
	VertexID string
}

Result is the information which endure sends to the user

type Service

type Service interface {
	// Serve starts the plugin
	Serve() chan error
	// Stop stops the plugin
	Stop(context.Context) error
}

Service interface can be implemented by the plugin to use Start-Stop functionality

type Weighted

type Weighted interface {
	Weight() uint
}

Weighted is optional to implement, but when implemented the return value added during the topological sort

Directories

Path Synopsis
tests

Jump to

Keyboard shortcuts

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