Reinforcer is a code generation tool that automates middleware injection in a proxy service that fronts your delegate implementation, this aids in building more resilient code as you can use common resiliency patterns in the middlewares such as circuit breakers, retrying, timeouts and others.
NOTE: While version is < 1.0.0 the APIs might dramatically change between minor versions, any breaking changes will be enumerated here starting with version 0.7.0 and forward.
Visit the releases page for pre-built binaries for OS X, Linux and Windows.
Use the Docker Image:
docker pull clear-street/reinforcer
Install through Homebrew
brew tap clear-street/reinforcer && brew install reinforcer
brew upgrade clear-street/reinforcer/reinforcer
Generate reinforced code for all exported interfaces and structs:
reinforcer --src=./service.go --targetall --outputdir=./reinforced
Generate reinforced code using regex:
reinforcer --src=./service.go --target='.*Service' --outputdir=./reinforced
Generate reinforced code using an exact match:
reinforcer --src=./service.go --target=MyService --outputdir=./reinforced
For more options:
reinforcer --help
Reinforcer is a CLI tool that generates code from interfaces that
will automatically inject middleware. Middlewares provide resiliency constructs
such as circuit breaker, retries, timeouts, etc.
Usage:
reinforcer [flags]
Flags:
--config string config file (default is $HOME/.reinforcer.yaml)
-d, --debug enables debug logs
-h, --help help for reinforcer
-i, --ignorenoret ignores methods that don't return anything (they won't be wrapped in the middleware). By default they'll be wrapped in a middleware and if the middleware emits an error the call will panic.
-p, --outpkg string name of generated package (default "reinforced")
-o, --outputdir string directory to write the generated code to (default "./reinforced")
-q, --silent disables logging. Mutually exclusive with the debug flag.
-s, --src strings source files to scan for the target interface or struct. If unspecified the file pointed by the env variable GOFILE will be used.
-k, --srcpkg strings source packages to scan for the target interface or struct.
-t, --target strings name of target type or regex to match interface or struct names with
-a, --targetall codegen for all exported interfaces/structs discovered. This option is mutually exclusive with the target option.
-v, --version show reinforcer's version
- Describe the target that you want to generate code for:
type Client interface {
DoOperation(ctx context.Context, arg string) error
}
Or from a struct:
type Client struct {
}
func (c *Client) DoOperation(ctx context.Context, arg string) error {
// ...
return nil
}
- Generate the reinforcer code:
reinforcer --debug --src='./client.go' --target=Client --outputdir=./reinforced
- Create the runner/middleware factory with the middlewares you want to inject into the generated code:
r := runner.NewFactory(
metrics.NewMiddleware(...),
circuitbreaker.NewMiddleware(...),
bulkhead.NewMiddleware(...),
retry.NewMiddleware(...),
timeout.NewMiddleware(...),
)
- Optionally create your predicate for errors that shouldn't be retried
// shouldRetryErrPredicate is a predicate that ignores the "NotFound" errors emited by the DoOperation in Client. All other errors
// are eligible for the middlewares.
shouldRetryErrPredicate := func(method string, err error) bool {
if method == reinforced.ClientMethods.DoOperation && errors.Is(client.NotFound, err) {
return false
}
return true
}
- Wrap the "real"/unrealiable implementation in the generated code:
c := client.NewClient(...)
// reinforcedClient implements the target interface so it can now be used in lieau of any place where the unreliable
// client was used
reinforcedClient := reinforced.NewClient(c, r, reinforced.WithRetryableErrorPredicate(shouldRetryErrPredicate))
A complete example is here