使用Uber FX提供一个接口

huangapple go评论93阅读模式
英文:

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 (
	&quot;time&quot;

	&quot;github.com/gofiber/fiber/v2&quot;
)


type BaseConfig interface {
	GetPort() string
	GetTimeout() int
}

type Config struct {
	Port           string `env:&quot;LISTEN_ADDR&quot; envDefault:&quot;:3000&quot;`
	Timeout        uint64 `env:&quot;TIMEOUT&quot; envDefault:&quot;10&quot;`
}

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 (
	&quot;github.com/caarlos0/env/v6&quot;
	&quot;github.com/rs/zerolog/log&quot;
)

type Config struct {
	Port                string        `env:&quot;LISTEN_ADDR&quot; envDefault:&quot;:3000&quot;`
	Timeout             uint64        `env:&quot;TIMEOUT&quot; envDefault:&quot;10&quot;`
    // some service specific stuff as well
}

func Parse() (*Config, error) {
	cfg := Config{}

	if err := env.Parse(&amp;cfg); err != nil {
		return nil, err
	}

	return &amp;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 (
	&quot;context&quot;
	&quot;time&quot;

	&quot;some-path/http&quot;
	&quot;some-path/config&quot;
    &quot;some-path/controllers&quot;
	&quot;github.com/gofiber/fiber/v2&quot;
	&quot;go.uber.org/fx&quot;
)

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 &lt;- app.Listen(config.Port)
			}()

			select {
			case err := &lt;-errChan:
				return err
			case &lt;-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 &quot;some-path/config&quot;

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 (
	&quot;time&quot;

	&quot;github.com/caarlos0/env/v6&quot;
	&quot;github.com/gofiber/fiber/v2&quot;
)

type BaseConfig interface {
	GetPort() string
	GetTimeout() int
}

type Config struct {
	Port    string `env:&quot;LISTEN_ADDR&quot; envDefault:&quot;:3000&quot;`
	Timeout uint64 `env:&quot;TIMEOUT&quot; envDefault:&quot;10&quot;`
}

func NewConfig() (*Config, error) {
	cfg := Config{}

	if err := env.Parse(&amp;cfg); err != nil {
		return nil, err
	}

	return &amp;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),
	}
}

huangapple
  • 本文由 发表于 2022年3月1日 20:44:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/71308704.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定