在 Viper 中处理两个数据库配置的最佳方法:解组为结构或使用 viper.get(Key)

问题描述 投票:0回答:1

我正在开发一个连接到 MySQL 和 PostgreSQL 数据库的应用程序。我正在使用 Viper 来管理两个数据库的配置,我不确定是否最好将配置解组到结构中并将其传递给连接函数,或者直接使用每个数据库中的 viper.Get("Key") 检索值功能。

这是两种上下文方法的示例:

方法 1:使用结构体加载配置并将其传递给函数:

// Struct with configuration for MySQL and PostgreSQL
type Config struct {
    MysqlHost string `mapstructure:"MYSQL_HOST"`
    MysqlPort int    `mapstructure:"MYSQL_PORT"`
    MysqlUser string `mapstructure:"MYSQL_USER"`
    MysqlPwd  string `mapstructure:"MYSQL_PASSWORD"`
    MysqlName string `mapstructure:"MYSQL_NAME"`
    PgHost  string `mapstructure:"PG_HOST"`
    PgPort  int    `mapstructure:"PG_PORT"`
    PgUser  string `mapstructure:"PG_USER"`
    PgPwd   string `mapstructure:"PG_PASSWORD"`
    PgName  string `mapstructure:"PG_NAME"`
}

// Load configuration into struct
func loadConfig(path string) (Config, error) {
    viper.AddConfigPath(path)
    viper.SetConfigName("pat")
    viper.SetConfigType("env")
    viper.AutomaticEnv()

    var config Config
    err := viper.ReadInConfig()
    if err != nil {
        return config, err
    }
    err = viper.Unmarshal(&config)
    return config, err
}

func connectMySQL(config Config) (*sqlx.DB, error) {
    mysqlConn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", 
        config.MysqlUser, config.MysqlPwd, config.MysqlHost, config.MysqlPort, config.MysqlName)
    return sqlx.Connect("mysql", mysqlConn)
}

func connectPostgres(config Config) (*sqlx.DB, error) {
    psqlConn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", 
        config.PgHost, config.PgPort, config.PgUser, config.PgPwd, config.PgName)
    return sqlx.Connect("postgres", psqlConn)
}

方法 2:直接在连接函数中使用 viper.Get(Key):

func connectMySQL() (*sqlx.DB, error) {
    mysqlConn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", 
                      viper.GetString("MYSQL_USER"), 
                      viper.GetString("MYSQL_PASSWORD"), 
                      viper.GetString("MYSQL_HOST"), 
                      viper.GetInt("MYSQL_PORT"), 
                      viper.GetString("MYSQL_NAME"))
    return sqlx.Connect("mysql", mysqlConn)
}

func connectPostgres() (*sqlx.DB, error) {
    psqlConn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", 
                     viper.GetString("PG_HOST"), 
                     viper.GetInt("PG_PORT"), 
                     viper.GetString("PG_USER"), 
                     viper.GetString("PG_PASSWORD"), 
                     viper.GetString("PG_NAME"))
    return sqlx.Connect("postgres", psqlConn)
}

虽然为了简单起见,我通常更喜欢第一种方法(解组),但使用 viper.Get(Key) 的全局配置来避免在函数之间共享配置结构是否有优势?

此外,在处理大型配置时,任何一种方法在性能、代码可读性、可维护性或灵活性方面是否有特定的优势?

有更好的方法吗?

go unmarshalling viper-go
1个回答
0
投票

我建议使用一些 DTO 来读取配置文件,然后将值放入具有非导出属性和导出 getter 的 Config 对象中。

此方法有助于消除读取后更改配置值的可能性。并且还有助于读取配置并忘记所有进一步用法的字段名称。

// DatabaseConfig is a unified connection config for an MySQL/Postgres databases.
type DatabaseConfig struct {
    host string
    port int
    user string
    pass string
    name string
}

func (c DatabaseConfig) GetHost() string {
    return c.host
}

func (c DatabaseConfig) GetPort() int {
    return c.port
}

func (c DatabaseConfig) GetUser() string {
    return c.user
}

func (c DatabaseConfig) GetPassword() string {
    return c.pass
}

func (c DatabaseConfig) GetName() string {
    return c.name
}

// Config is a full application config.
type Config struct {
    mySQL    DatabaseConfig
    postgres DatabaseConfig
}

func (c Config) GetMySQLConfig() DatabaseConfig {
    return c.mySQL
}

func (c Config) GetPostgresConfig() DatabaseConfig {
    return c.postgres
}

type configDTO struct {
    MysqlHost string `mapstructure:"MYSQL_HOST"`
    MysqlPort int    `mapstructure:"MYSQL_PORT"`
    MysqlUser string `mapstructure:"MYSQL_USER"`
    MysqlPwd  string `mapstructure:"MYSQL_PASSWORD"`
    MysqlName string `mapstructure:"MYSQL_NAME"`
    PgHost    string `mapstructure:"PG_HOST"`
    PgPort    int    `mapstructure:"PG_PORT"`
    PgUser    string `mapstructure:"PG_USER"`
    PgPwd     string `mapstructure:"PG_PASSWORD"`
    PgName    string `mapstructure:"PG_NAME"`
}

func loadConfig(path string) (Config, error) {
    vpr := viper.New()

    vpr.AddConfigPath(path)
    vpr.SetConfigName("pat")
    vpr.SetConfigType("env")
    vpr.AutomaticEnv()

    var dto configDTO

    if err := vpr.ReadInConfig(); err != nil {
        return Config{}, fmt.Errorf("failed to read config file: %w", err)
    }

    if err := vpr.Unmarshal(&dto); err != nil {
        return Config{}, fmt.Errorf("failed to unmarshal config: %w", err)
    }

    return Config{
        mySQL: DatabaseConfig{
            host: dto.MysqlHost,
            port: dto.MysqlPort,
            user: dto.MysqlUser,
            pass: dto.MysqlPwd,
            name: dto.MysqlPwd,
        },
        postgres: DatabaseConfig{
            host: dto.PgHost,
            port: dto.PgPort,
            user: dto.PgUser,
            pass: dto.PgPwd,
            name: dto.PgPwd,
        },
    }, nil
}

// connectMySQL(config.GetMySQLConfig())
func connectMySQL(config DatabaseConfig) (*sqlx.DB, error) {
    mysqlConn := fmt.Sprintf(
        "%s:%s@tcp(%s:%d)/%s",
        config.GetUser(), config.GetPassword(), config.GetHost(), config.GetPort(), config.GetName(),
    )

    return sqlx.Connect("mysql", mysqlConn)
}

// connectPostgres(config.GetPostgresConfig())
func connectPostgres(config DatabaseConfig) (*sqlx.DB, error) {
    psqlConn := fmt.Sprintf(
        "host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
        config.GetHost(), config.GetPort(), config.GetUser(), config.GetPassword(), config.GetName(),
    )

    return sqlx.Connect("postgres", psqlConn)
© www.soinside.com 2019 - 2024. All rights reserved.