英文:
Using uber fx to provide an interface
问题
我正在尝试使用uber fx为Go微服务项目进行依赖注入。
由于所有的微服务都需要构建一个基础服务器,并设置各种配置选项(如常见的中间件、缓冲区大小等)(我正在使用fiber)。但是这些不同的微服务也有各自特定的配置选项,比如数据库连接字符串、JWT密钥等。
我创建了一个接口,在共享函数中使用该接口创建通用的基础应用程序,并带有共同的选项,但是任何需要依赖配置结构的函数都会失败,因为它们期望特定版本的微服务配置。
> failed to build *fiber.App: missing dependencies for function "some-path/http".CreateServer (some-path/http/http.go:65): missing type: *http.Config
exit status 1
最小示例:
http/http.go
package http
import (
"time"
"github.com/gofiber/fiber/v2"
)
type BaseConfig interface {
GetPort() string
GetTimeout() int
}
type Config struct {
Port string `env:"LISTEN_ADDR" envDefault:":3000"`
Timeout uint64 `env:"TIMEOUT" envDefault:"10"`
}
func (c *Config) GetPort() string {
return c.Port
}
func (c *Config) GetTimeout() int {
return int(c.Timeout)
}
func CreateServer(config *Config) *fiber.App {
fiberConfig := fiber.Config{
ReadTimeout: time.Second * time.Duration(config.GetTimeout()),
WriteTimeout: time.Second * time.Duration(config.GetTimeout()),
}
app := fiber.New(fiberConfig)
// do setup and other stuff
return app
}
some-service/config/config.go
package config
import (
"github.com/caarlos0/env/v6"
"github.com/rs/zerolog/log"
)
type Config struct {
Port string `env:"LISTEN_ADDR" envDefault:":3000"`
Timeout uint64 `env:"TIMEOUT" envDefault:"10"`
// some service specific stuff as well
}
func Parse() (*Config, error) {
cfg := Config{}
if err := env.Parse(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}
func (c *Config) GetPort() string {
return c.Port
}
func (c *Config) GetTimeout() int {
return int(c.Timeout)
}
some-service/main.go
package main
import (
"context"
"time"
"some-path/http"
"some-path/config"
"some-path/controllers"
"github.com/gofiber/fiber/v2"
"go.uber.org/fx"
)
func main() {
opts := []fx.Option{}
opts = append(opts, provideOptions()...)
opts = append(opts, fx.Invoke(run))
app := fx.New(opts...)
app.Run()
}
func provideOptions() []fx.Option {
return []fx.Option{
fx.Invoke(utils.ConfigureLogger),
fx.Provide(config.Parse),
fx.Invoke(controllers.SomeController),
}
}
func run(app *fiber.App, config *config.Config, lc fx.Lifecycle) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
errChan := make(chan error)
go func() {
errChan <- app.Listen(config.Port)
}()
select {
case err := <-errChan:
return err
case <-time.After(100 * time.Millisecond):
return nil
}
},
OnStop: func(ctx context.Context) error {
return app.Shutdown()
},
})
}
some-path/controllers/some-controller.go
package controllers
import "some-path/config"
func SomeController (config *config.Config) {
// do stuff
}
英文:
I am attempting to use uber fx to do dependency injection for a go microservice project.
Since all the microservices will need to construct a base server, and set up a variety of config options (common middleware, buffer sizes etc) (I am using fiber). But these different microservices also have config options unique to the microservice. Maybe a database connection string, jwt keys etc.
I created an interface to use in the shared function that creates the common base app, with the common options, but any function needing a dependency of the config struct fails with expecting the specific version of config for that microservice.
> failed to build *fiber.App: missing dependencies for function "some-path/http".CreateServer (some-path/http/http.go:65): missing type: *http.Config
exit status 1
Minimal example:
http/http.go
package http
import (
"time"
"github.com/gofiber/fiber/v2"
)
type BaseConfig interface {
GetPort() string
GetTimeout() int
}
type Config struct {
Port string `env:"LISTEN_ADDR" envDefault:":3000"`
Timeout uint64 `env:"TIMEOUT" envDefault:"10"`
}
func (c *Config) GetPort() string {
return c.Port
}
func (c *Config) GetTimeout() int {
return int(c.Timeout)
}
func CreateServer(config *Config) *fiber.App {
fiberConfig := fiber.Config{
ReadTimeout: time.Second * time.Duration(config.GetTimeout()),
WriteTimeout: time.Second * time.Duration(config.GetTimeout()),
}
app := fiber.New(fiberConfig)
// do setup and other stuff
return app
}
some-service/config/config.go
package config
import (
"github.com/caarlos0/env/v6"
"github.com/rs/zerolog/log"
)
type Config struct {
Port string `env:"LISTEN_ADDR" envDefault:":3000"`
Timeout uint64 `env:"TIMEOUT" envDefault:"10"`
// some service specific stuff as well
}
func Parse() (*Config, error) {
cfg := Config{}
if err := env.Parse(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}
func (c *Config) GetPort() string {
return c.Port
}
func (c *Config) GetTimeout() int {
return int(c.Timeout)
}
some-service/main.go
package main
import (
"context"
"time"
"some-path/http"
"some-path/config"
"some-path/controllers"
"github.com/gofiber/fiber/v2"
"go.uber.org/fx"
)
func main() {
opts := []fx.Option{}
opts = append(opts, provideOptions()...)
opts = append(opts, fx.Invoke(run))
app := fx.New(opts...)
app.Run()
}
func provideOptions() []fx.Option {
return []fx.Option{
fx.Invoke(utils.ConfigureLogger),
fx.Provide(config.Parse),
fx.Invoke(controllers.SomeController),
}
}
func run(app *fiber.App, config *config.Config, lc fx.Lifecycle) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
errChan := make(chan error)
go func() {
errChan <- app.Listen(config.Port)
}()
select {
case err := <-errChan:
return err
case <-time.After(100 * time.Millisecond):
return nil
}
},
OnStop: func(ctx context.Context) error {
return app.Shutdown()
},
})
}
some-path/controllers/some-controller.go
package controllers
import "some-path/config"
func SomeController (config *config.Config) {
// do stuff
}
</details>
# 答案1
**得分**: 0
你缺少`*http.Config`对象,请创建一个返回该对象的函数,例如`NewConfig()`。
```go
package http
import (
"time"
"github.com/caarlos0/env/v6"
"github.com/gofiber/fiber/v2"
)
type BaseConfig interface {
GetPort() string
GetTimeout() int
}
type Config struct {
Port string `env:"LISTEN_ADDR" envDefault:":3000"`
Timeout uint64 `env:"TIMEOUT" envDefault:"10"`
}
func NewConfig() (*Config, error) {
cfg := Config{}
if err := env.Parse(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}
func (c *Config) GetPort() string {
return c.Port
}
func (c *Config) GetTimeout() int {
return int(c.Timeout)
}
func CreateServer(config *Config) *fiber.App {
fiberConfig := fiber.Config{
ReadTimeout: time.Second * time.Duration(config.GetTimeout()),
WriteTimeout: time.Second * time.Duration(config.GetTimeout()),
}
app := fiber.New(fiberConfig)
// 进行设置和其他操作
return app
}
然后修改你的provideOptions()
函数,可能像这样:
func provideOptions() []fx.Option {
return []fx.Option{
fx.Invoke(utils.ConfigureLogger),
fx.Provide(config.Parse, http.NewConfig),
fx.Invoke(controllers.SomeController),
fx.Provide(http.CreateServer),
}
}
英文:
You're missing *http.Config
object, create a function that return that object, e.g. NewConfig()
package http
import (
"time"
"github.com/caarlos0/env/v6"
"github.com/gofiber/fiber/v2"
)
type BaseConfig interface {
GetPort() string
GetTimeout() int
}
type Config struct {
Port string `env:"LISTEN_ADDR" envDefault:":3000"`
Timeout uint64 `env:"TIMEOUT" envDefault:"10"`
}
func NewConfig() (*Config, error) {
cfg := Config{}
if err := env.Parse(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}
func (c *Config) GetPort() string {
return c.Port
}
func (c *Config) GetTimeout() int {
return int(c.Timeout)
}
func CreateServer(config *Config) *fiber.App {
fiberConfig := fiber.Config{
ReadTimeout: time.Second * time.Duration(config.GetTimeout()),
WriteTimeout: time.Second * time.Duration(config.GetTimeout()),
}
app := fiber.New(fiberConfig)
// do setup and other stuff
return app
}
then change your provideOptions()
, maybe like this:
func provideOptions() []fx.Option {
return []fx.Option{
fx.Invoke(utils.ConfigureLogger),
fx.Provide(config.Parse, http.NewConfig),
fx.Invoke(controllers.SomeController),
fx.Provide(http.CreateServer),
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论