Skip to content

engine_1.1

Latest
Compare
Choose a tag to compare
@autom8ter autom8ter released this 23 Mar 19:14
· 38 commits to master since this release

Engine

A Pluggable gRPC Microservice Framework

go get github.com/autom8ter/engine

Contributers: Coleman Word

License: MIT

//Exported variable named Plugin, used to build with go/plugin
//Compile plugin and add to your config path to be loaded by the engine instance
// ex: go build -buildmode=plugin -o bin/example.so examplepb/plugin.go
var Plugin  Example

//Embeded driver.PluginFunc is used to satisfy the driver.Plugin interface
type Example struct {
	driver.PluginFunc
}
//
func NewExample() Example {
	e := Example{}
	e.PluginFunc = func(s *grpc.Server) {
		examplepb.RegisterEchoServiceServer(s, e)
	}
	return e
}
//examplepb methods excluded for brevity

//The compiled plugin file will be loaded at runtime if its set in your config path.
//A basic example with all config options:
func main() {
	// consider using flags, env vars. or a config file to populate the inputs needed to create an engine instance
    	if err := engine.New("tcp", ":3002", "Plugin").With(
    		//verbose logging for development
    		config.WithDebug(),
    		config.WithStatsHandler(nil),
    		//connection timeout
    		config.WithConnTimeout(2 *time.Minute),
    		//transport credentials ref:https://godoc.org/google.golang.org/grpc/credentials
    		config.WithCreds(nil),
    		//max concurrent streams
    		config.WithMaxConcurrentStreams(1000),
    		//filepath to plugin
    		config.WithPluginPaths("bin/example.so"),
    	).Serve(); err != nil {
    		grpclog.Fatalln(err.Error())
    	}
}

Table of Contents


Overview

  • Engine serves go/plugins that are dynamically loaded at runtime.
  • Plugins must export a type that implements the driver.Plugin interface: RegisterWithServer(s *grpc.Server)
  • Engine decouples the server runtime from grpc service development so that plugins can be added as externally compiled files that can be added to a deployment from local storage without making changes to the engine server.

Features/Scope/Roadmap


Driver

github.com/autom8ter/engine/driver

driver.Plugin is used to register grpc server implementations.

//Plugin is an interface for representing gRPC server implementations.
type Plugin interface {
	RegisterWithServer(*grpc.Server)
}

//PluginFunc implements the Plugin interface.
type PluginFunc func(*grpc.Server)

//RegisterWithServer is an interface for representing gRPC server implementations.
func (p PluginFunc) RegisterWithServer(s *grpc.Server) {
	p(s)
}

Grpc Middlewares

Middlewares should be used for things like monitoring, logging, auth, retry, etc.

They can be added to the engine with:

config.WithStreamInterceptors(...)
config.WithUnaryInterceptors(...)

Key Functions:

type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error
type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)

Example(recovery):

// UnaryServerInterceptor returns a new unary server interceptor for panic recovery.
func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor {
	o := evaluateOptions(opts)
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) {
		defer func() {
			if r := recover(); r != nil {
				err = recoverFrom(ctx, r, o.recoveryHandlerFunc)
			}
		}()

		return handler(ctx, req)
	}
}

// StreamServerInterceptor returns a new streaming server interceptor for panic recovery.
func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor {
	o := evaluateOptions(opts)
	return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) {
		defer func() {
			if r := recover(); r != nil {
				err = recoverFrom(stream.Context(), r, o.recoveryHandlerFunc)
			}
		}()

		return handler(srv, stream)
	}
}

Please see go-grpc-middleware for a list of useful
Unary and Streaming Interceptors


GoDoc

type Engine

type Engine interface {
	With(opts ...config.Option) *Runtime
	Config() *config.Config
	Shutdown()
	Serve() error
}

Engine is an interface used to describe a server runtime

func New

func New(network, addr, symbol string, paths ...string) Engine

New creates a engine intstance.

type Runtime

type Runtime struct {
}

Runtime is an implementation of the engine API.

func (*Runtime) Config

func (e *Runtime) Config() *config.Config

Config returns the runtimes current configuration

func (*Runtime) Serve

func (e *Runtime) Serve() error

Serve starts the runtime gRPC server.

func (*Runtime) Shutdown

func (e *Runtime) Shutdown()

Shutdown gracefully closes the grpc server.

func (*Runtime) With

func (e *Runtime) With(opts ...config.Option) *Runtime

With wraps the runtimes config with config options ref:
github.com/autom8ter/engine/config/options.go


Limitations

Im hoping someone can help explain why some of these errors occur:

  • When creating a plugin, one must NOT use pointer methods when satisfying the driver.Plugin interface
  • If a json config is hard-coded as a string, the server fails, but succeeds if it is present as a config file