使用Uber FX提供一个接口

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

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

  1. package http
  2. import (
  3. "time"
  4. "github.com/gofiber/fiber/v2"
  5. )
  6. type BaseConfig interface {
  7. GetPort() string
  8. GetTimeout() int
  9. }
  10. type Config struct {
  11. Port string `env:"LISTEN_ADDR" envDefault:":3000"`
  12. Timeout uint64 `env:"TIMEOUT" envDefault:"10"`
  13. }
  14. func (c *Config) GetPort() string {
  15. return c.Port
  16. }
  17. func (c *Config) GetTimeout() int {
  18. return int(c.Timeout)
  19. }
  20. func CreateServer(config *Config) *fiber.App {
  21. fiberConfig := fiber.Config{
  22. ReadTimeout: time.Second * time.Duration(config.GetTimeout()),
  23. WriteTimeout: time.Second * time.Duration(config.GetTimeout()),
  24. }
  25. app := fiber.New(fiberConfig)
  26. // do setup and other stuff
  27. return app
  28. }

some-service/config/config.go

  1. package config
  2. import (
  3. "github.com/caarlos0/env/v6"
  4. "github.com/rs/zerolog/log"
  5. )
  6. type Config struct {
  7. Port string `env:"LISTEN_ADDR" envDefault:":3000"`
  8. Timeout uint64 `env:"TIMEOUT" envDefault:"10"`
  9. // some service specific stuff as well
  10. }
  11. func Parse() (*Config, error) {
  12. cfg := Config{}
  13. if err := env.Parse(&cfg); err != nil {
  14. return nil, err
  15. }
  16. return &cfg, nil
  17. }
  18. func (c *Config) GetPort() string {
  19. return c.Port
  20. }
  21. func (c *Config) GetTimeout() int {
  22. return int(c.Timeout)
  23. }

some-service/main.go

  1. package main
  2. import (
  3. "context"
  4. "time"
  5. "some-path/http"
  6. "some-path/config"
  7. "some-path/controllers"
  8. "github.com/gofiber/fiber/v2"
  9. "go.uber.org/fx"
  10. )
  11. func main() {
  12. opts := []fx.Option{}
  13. opts = append(opts, provideOptions()...)
  14. opts = append(opts, fx.Invoke(run))
  15. app := fx.New(opts...)
  16. app.Run()
  17. }
  18. func provideOptions() []fx.Option {
  19. return []fx.Option{
  20. fx.Invoke(utils.ConfigureLogger),
  21. fx.Provide(config.Parse),
  22. fx.Invoke(controllers.SomeController),
  23. }
  24. }
  25. func run(app *fiber.App, config *config.Config, lc fx.Lifecycle) {
  26. lc.Append(fx.Hook{
  27. OnStart: func(ctx context.Context) error {
  28. errChan := make(chan error)
  29. go func() {
  30. errChan <- app.Listen(config.Port)
  31. }()
  32. select {
  33. case err := <-errChan:
  34. return err
  35. case <-time.After(100 * time.Millisecond):
  36. return nil
  37. }
  38. },
  39. OnStop: func(ctx context.Context) error {
  40. return app.Shutdown()
  41. },
  42. })
  43. }

some-path/controllers/some-controller.go

  1. package controllers
  2. import "some-path/config"
  3. func SomeController (config *config.Config) {
  4. // do stuff
  5. }
英文:

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

  1. package http
  2. import (
  3. &quot;time&quot;
  4. &quot;github.com/gofiber/fiber/v2&quot;
  5. )
  6. type BaseConfig interface {
  7. GetPort() string
  8. GetTimeout() int
  9. }
  10. type Config struct {
  11. Port string `env:&quot;LISTEN_ADDR&quot; envDefault:&quot;:3000&quot;`
  12. Timeout uint64 `env:&quot;TIMEOUT&quot; envDefault:&quot;10&quot;`
  13. }
  14. func (c *Config) GetPort() string {
  15. return c.Port
  16. }
  17. func (c *Config) GetTimeout() int {
  18. return int(c.Timeout)
  19. }
  20. func CreateServer(config *Config) *fiber.App {
  21. fiberConfig := fiber.Config{
  22. ReadTimeout: time.Second * time.Duration(config.GetTimeout()),
  23. WriteTimeout: time.Second * time.Duration(config.GetTimeout()),
  24. }
  25. app := fiber.New(fiberConfig)
  26. // do setup and other stuff
  27. return app
  28. }

some-service/config/config.go

  1. package config
  2. import (
  3. &quot;github.com/caarlos0/env/v6&quot;
  4. &quot;github.com/rs/zerolog/log&quot;
  5. )
  6. type Config struct {
  7. Port string `env:&quot;LISTEN_ADDR&quot; envDefault:&quot;:3000&quot;`
  8. Timeout uint64 `env:&quot;TIMEOUT&quot; envDefault:&quot;10&quot;`
  9. // some service specific stuff as well
  10. }
  11. func Parse() (*Config, error) {
  12. cfg := Config{}
  13. if err := env.Parse(&amp;cfg); err != nil {
  14. return nil, err
  15. }
  16. return &amp;cfg, nil
  17. }
  18. func (c *Config) GetPort() string {
  19. return c.Port
  20. }
  21. func (c *Config) GetTimeout() int {
  22. return int(c.Timeout)
  23. }

some-service/main.go

  1. package main
  2. import (
  3. &quot;context&quot;
  4. &quot;time&quot;
  5. &quot;some-path/http&quot;
  6. &quot;some-path/config&quot;
  7. &quot;some-path/controllers&quot;
  8. &quot;github.com/gofiber/fiber/v2&quot;
  9. &quot;go.uber.org/fx&quot;
  10. )
  11. func main() {
  12. opts := []fx.Option{}
  13. opts = append(opts, provideOptions()...)
  14. opts = append(opts, fx.Invoke(run))
  15. app := fx.New(opts...)
  16. app.Run()
  17. }
  18. func provideOptions() []fx.Option {
  19. return []fx.Option{
  20. fx.Invoke(utils.ConfigureLogger),
  21. fx.Provide(config.Parse),
  22. fx.Invoke(controllers.SomeController),
  23. }
  24. }
  25. func run(app *fiber.App, config *config.Config, lc fx.Lifecycle) {
  26. lc.Append(fx.Hook{
  27. OnStart: func(ctx context.Context) error {
  28. errChan := make(chan error)
  29. go func() {
  30. errChan &lt;- app.Listen(config.Port)
  31. }()
  32. select {
  33. case err := &lt;-errChan:
  34. return err
  35. case &lt;-time.After(100 * time.Millisecond):
  36. return nil
  37. }
  38. },
  39. OnStop: func(ctx context.Context) error {
  40. return app.Shutdown()
  41. },
  42. })
  43. }

some-path/controllers/some-controller.go

  1. package controllers
  2. import &quot;some-path/config&quot;
  3. func SomeController (config *config.Config) {
  4. // do stuff
  5. }
  6. </details>
  7. # 答案1
  8. **得分**: 0
  9. 你缺少`*http.Config`对象,请创建一个返回该对象的函数,例如`NewConfig()`
  10. ```go
  11. package http
  12. import (
  13. "time"
  14. "github.com/caarlos0/env/v6"
  15. "github.com/gofiber/fiber/v2"
  16. )
  17. type BaseConfig interface {
  18. GetPort() string
  19. GetTimeout() int
  20. }
  21. type Config struct {
  22. Port string `env:"LISTEN_ADDR" envDefault:":3000"`
  23. Timeout uint64 `env:"TIMEOUT" envDefault:"10"`
  24. }
  25. func NewConfig() (*Config, error) {
  26. cfg := Config{}
  27. if err := env.Parse(&cfg); err != nil {
  28. return nil, err
  29. }
  30. return &cfg, nil
  31. }
  32. func (c *Config) GetPort() string {
  33. return c.Port
  34. }
  35. func (c *Config) GetTimeout() int {
  36. return int(c.Timeout)
  37. }
  38. func CreateServer(config *Config) *fiber.App {
  39. fiberConfig := fiber.Config{
  40. ReadTimeout: time.Second * time.Duration(config.GetTimeout()),
  41. WriteTimeout: time.Second * time.Duration(config.GetTimeout()),
  42. }
  43. app := fiber.New(fiberConfig)
  44. // 进行设置和其他操作
  45. return app
  46. }

然后修改你的provideOptions()函数,可能像这样:

  1. func provideOptions() []fx.Option {
  2. return []fx.Option{
  3. fx.Invoke(utils.ConfigureLogger),
  4. fx.Provide(config.Parse, http.NewConfig),
  5. fx.Invoke(controllers.SomeController),
  6. fx.Provide(http.CreateServer),
  7. }
  8. }
英文:

You're missing *http.Config object, create a function that return that object, e.g. NewConfig()

  1. package http
  2. import (
  3. &quot;time&quot;
  4. &quot;github.com/caarlos0/env/v6&quot;
  5. &quot;github.com/gofiber/fiber/v2&quot;
  6. )
  7. type BaseConfig interface {
  8. GetPort() string
  9. GetTimeout() int
  10. }
  11. type Config struct {
  12. Port string `env:&quot;LISTEN_ADDR&quot; envDefault:&quot;:3000&quot;`
  13. Timeout uint64 `env:&quot;TIMEOUT&quot; envDefault:&quot;10&quot;`
  14. }
  15. func NewConfig() (*Config, error) {
  16. cfg := Config{}
  17. if err := env.Parse(&amp;cfg); err != nil {
  18. return nil, err
  19. }
  20. return &amp;cfg, nil
  21. }
  22. func (c *Config) GetPort() string {
  23. return c.Port
  24. }
  25. func (c *Config) GetTimeout() int {
  26. return int(c.Timeout)
  27. }
  28. func CreateServer(config *Config) *fiber.App {
  29. fiberConfig := fiber.Config{
  30. ReadTimeout: time.Second * time.Duration(config.GetTimeout()),
  31. WriteTimeout: time.Second * time.Duration(config.GetTimeout()),
  32. }
  33. app := fiber.New(fiberConfig)
  34. // do setup and other stuff
  35. return app
  36. }

then change your provideOptions(), maybe like this:

  1. func provideOptions() []fx.Option {
  2. return []fx.Option{
  3. fx.Invoke(utils.ConfigureLogger),
  4. fx.Provide(config.Parse, http.NewConfig),
  5. fx.Invoke(controllers.SomeController),
  6. fx.Provide(http.CreateServer),
  7. }
  8. }

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:

确定