fx
fx copied to clipboard
Example of using fx for a short-lived CLI process?
All of the information/examples that I can find about how to use Fx in the "correct" way are centered around long-lived processes (HTTP servers and the like). I really like how my application is structured with Fx, but I'm not quite sure how to use it for e.g. a CLI application that should be running for < 1 second.
For instance, is there any way to silence the log output entirely? Is there a way to default to not a long-running process?
As of now, there isn't really a great way. Fx is designed to be more suitable for long-lived processes because it was mainly designed as a DI framework for microservices at Uber, which are, long-lived processes :).
Enabling fx to be more friendly for short-lived processes has been an ongoing discussion but there is no planned timeline on that happening.
Fx works quite well with short lived processes thanks to very handy Shutdowner
interface. Yes, the boilerplate could be leaner, but the approach definitely woks.
@oakad can you give us concrete working example?
package main
import (
"fmt"
"time"
"context"
"go.uber.org/fx"
)
type Action struct {
sh fx.Shutdowner
}
func newAction(
lc fx.Lifecycle,
sh fx.Shutdowner,
) *Action {
act := &Action{
sh: sh,
}
lc.Append(fx.Hook{
OnStart: act.start,
OnStop: act.stop,
})
return act
}
func (act *Action) start(ctx context.Context) error {
go act.run()
return nil
}
func (act *Action) stop(ctx context.Context) error {
fmt.Println("Stopped")
return nil
}
func (act *Action) run() {
time.Sleep(time.Second)
fmt.Println("Will stop now")
act.sh.Shutdown()
}
func main() {
fx.New(
fx.Provide(
newAction,
),
fx.Invoke(func(*Action) {}),
).Run()
}
package main import ( "fmt" "time" "context" "go.uber.org/fx" ) type Action struct { sh fx.Shutdowner } func newAction( lc fx.Lifecycle, sh fx.Shutdowner, ) *Action { act := &Action{ sh: sh, } lc.Append(fx.Hook{ OnStart: act.start, OnStop: act.stop, }) return act } func (act *Action) start(ctx context.Context) error { go act.run() return nil } func (act *Action) stop(ctx context.Context) error { fmt.Println("Stopped") return nil } func (act *Action) run() { time.Sleep(time.Second) fmt.Println("Will stop now") act.sh.Shutdown() } func main() { fx.New( fx.Provide( newAction, ), fx.Invoke(func(*Action) {}), ).Run() }
could this be added to the documentation for short-lived applications
Here's another workaround for short-lived processes:
package main
import (
"context"
"fmt"
"go.uber.org/fx"
)
func Run(opts ...fx.Option) {
app := fx.New(opts...)
if err := app.Start(context.Background()); err != nil {
fmt.Println("[Fx] START FAILED\t" + err.Error())
return
}
if err := app.Stop(context.Background()); err != nil {
fmt.Println("[Fx] STOP FAILED\t" + err.Error())
return
}
fmt.Println("[Fx] DONE")
}
func main() {
Run(
fx.Invoke(func() {
fmt.Println("--> Hello world!")
}),
)
}