+
Skip to content

lftk/lcfx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lcfx

lcfx is a small helper library for the go.uber.org/fx dependency injection framework. It simplifies the management of long-running, asynchronous tasks (like web servers, message queue consumers, or other background services) within the fx application lifecycle.

The Problem

When using fx, OnStart hooks are expected to complete quickly. If a hook blocks for too long (by default, 15 seconds), the application will time out during startup and exit with an error. Starting a long-running service like an HTTP server directly in an OnStart hook will cause this timeout.

The common workaround is to launch the service in a separate goroutine. However, this requires manual wiring for graceful shutdown and a way to signal startup errors back to the main application, which can be complex.

The Solution: lcfx

lcfx provides a custom Lifecycle wrapper with an AppendAsync method. This allows you to write your long-running task logic in a simple, synchronous style, while lcfx handles the complexity of running it in the background and ensuring graceful shutdown.

To use it, you need to:

  1. Add lcfx.Module to your fx.New() call. This provides the custom lifecycle to the dependency graph.
  2. Request lcfx.Lifecycle in your constructor instead of fx.Lifecycle.
  3. Use the lc.AppendAsync method to register your long-running service.

Here is a complete example:

package main

import (
	"context"
	"errors"
	"fmt"
	"net/http"

	"github.com/lftk/lcfx" // Import lcfx
	"go.uber.org/fx"
)

func main() {
	app := fx.New(
		// 1. Add the lcfx.Module.
		lcfx.Module,

		fx.Provide(NewMux, NewHandler),
		fx.Invoke(Register),

		// Disable fx logging for clarity in this example.
		fx.NopLogger,
	)
	app.Run()
}

// 2. Request lcfx.Lifecycle in the constructor.
func NewMux(lc lcfx.Lifecycle) *http.ServeMux {
	mux := http.NewServeMux()
	server := &http.Server{
		Addr:    "127.0.0.1:8080",
		Handler: mux,
	}

	// 3. Use lc.AppendAsync for the long-running task.
	lc.AppendAsync(fx.Hook{
		OnStart: func(context.Context) error {
			fmt.Println("Starting HTTP server at", server.Addr)
			// Write blocking code directly. lcfx handles the goroutine.
			// If this returns an error, lcfx will shut down the app.
			err := server.ListenAndServe()
			if errors.Is(err, http.ErrServerClosed) {
				fmt.Println("HTTP server shut down gracefully.")
				return nil // Expected error on graceful shutdown.
			}
			return err
		},
		OnStop: func(ctx context.Context) error {
			fmt.Println("Stopping HTTP server.")
			return server.Shutdown(ctx)
		},
	})
	return mux
}

func NewHandler() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Got a request.")
		fmt.Fprintln(w, "Hello, world!")
	})
}

func Register(mux *http.ServeMux, h http.Handler) {
	mux.Handle("/", h)
}

Installation

go get github.com/lftk/lcfx

About

A go.uber.org/fx module for gracefully managing long-running services within the application lifecycle.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载