MX

A Go microservices framework with runtime launcher and services runner
Features
- Logger
- Launcher
- Services
- Services runner
-
Enabler interface
-
HealthChecker interface
- Metrics
- Health checker
- Liveness probe (
/livez)
- Readiness probe (
/readyz)
- Ping pong service
- Http transport
- GRPC transport
- GRPC client
- ConnectRPC transport
- ConnectRPC client
Launcher capabilities
| Capability |
Option / Interface |
Description |
| Lifecycle hooks |
WithBeforeStart, WithAfterStart, WithBeforeStop, WithAfterStop |
Global hooks around app start/stop |
| Service state machine |
svc.State() |
Tracks each service: idle → starting → running → stopping → stopped / failed |
| Service restart policy |
WithRestartPolicy(RestartPolicy{...}) |
RestartOnFailure / RestartAlways with exponential backoff |
| Startup timeout |
WithStartupTimeout(d) |
Fail the service if StartFn does not signal ready within d |
| Shutdown timeout (per service) |
WithShutdownTimeout(d) |
Max time to wait for a service to stop |
| Global shutdown timeout |
WithGlobalShutdownTimeout(d) |
Hard deadline for the entire graceful shutdown phase |
| Stop sequence |
WithRunnerServicesSequence(...) |
None (parallel) / Fifo / Lifo |
| Service lookup |
ServicesRunner().Get(name) |
Retrieve a registered service by name at runtime |
| Health checker |
types.HealthChecker interface |
Periodic per-service health check, polled on a configurable interval |
| Liveness probe |
ops /livez |
200 healthy / 503 if any service is in Failed state |
| Readiness probe |
ops /readyz |
200 ready / 424 starting / 503 failed — combines ServiceState + HealthChecker results |
| Legacy health endpoint |
ops /healthy |
Backward-compatible endpoint (HealthChecker results only) |
| Metrics |
ops /metrics |
Prometheus metrics endpoint |
| Profiler |
ops /debug/pprof |
Go pprof profiler endpoint |
How to use
Repo with example
Init launcher
var version = "local"
var appName = "mx-example"
logger := logger.New(
logger.WithAppVersion(version),
logger.WithAppName(appName),
)
ln := launcher.New(
launcher.WithName(appName),
launcher.WithLogger(logger),
launcher.WithVersion(version),
launcher.WithContext(context.Background()),
launcher.WithAfterStart(func() error {
logger.Infoln("app", appName, "was started")
return nil
}),
launcher.WithAfterStop(func() error {
logger.Infoln("app", appName, "was stopped")
return nil
}),
)
Init and register custom service
// init
svc := launcher.NewService(
launcher.WithServiceName("test-service"),
launcher.WithStart(func(_ context.Context) error {
return nil
}),
launcher.WithStop(func(_ context.Context) error {
time.Sleep(time.Second * 3)
return nil
}),
)
// register in launcher
ln.ServicesRunner().Register(svc)
Init and register ping pong service
import "github.com/tkcrm/mx/launcher/services/pingpong"
// init
pingPongSvc := launcher.NewService(launcher.WithService(pingpong.New(logger)))
// register in launcher
ln.ServicesRunner().Register(pingPongSvc)
Register any service that implements IService
Any struct with Name(), Start(), and Stop() methods satisfies types.IService and can be wrapped with launcher.NewService:
import "github.com/tkcrm/mx/launcher/types"
type books struct {
name string
hcInterval time.Duration
}
func New() *books {
return &books{
name: "books-service",
hcInterval: time.Second * 3,
}
}
func (s books) Name() string { return s.name }
func (s books) Healthy(ctx context.Context) error { return nil }
func (s books) Interval() time.Duration { return s.hcInterval }
func (s books) Start(ctx context.Context) error {
<-ctx.Done()
return nil
}
func (s books) Stop(ctx context.Context) error { return nil }
var _ types.HealthChecker = (*books)(nil)
var _ types.IService = (*books)(nil)
func main() {
ln := launcher.New()
// register service in launcher with health checker
ln.ServicesRunner().Register(
launcher.NewService(
launcher.WithService(New()),
),
)
}
Graceful shutdown
The first signal (SIGTERM / SIGINT / SIGQUIT) starts a graceful shutdown. A second signal forces immediate exit.
if err := ln.Run(); err != nil {
logger.Fatal(err)
}