我正在开发一个连接到 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) 的全局配置来避免在函数之间共享配置结构是否有优势?
此外,在处理大型配置时,任何一种方法在性能、代码可读性、可维护性或灵活性方面是否有特定的优势?
有更好的方法吗?
我建议使用一些 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)