自定义配置

项目中配置文件是必不可少的,本文主要讲解了如何使用dubbo-go现有的库加载并初始化自定义的配置文件

主要以配置 MySQL和 Redis 为例,完整代码见02-custom-config-file

编写配置文件

配置文件的格式有很多种,本文选择 yaml格式,其是JSON的超集,不仅提供了更加简洁易懂的语法,还具有极强的可读性并提供了更多功能,例如引用、类型的自动检测以及支持对齐的多行值。

接下来开始编写配置文件,在 conf 目录下新建 data.yml文件,并填入以下内容:

  1. mysql:
  2. dsn: "root:123456@tcp(127.0.0.1:3306)/study?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8mb4"
  3. log_mode: false
  4. max_open_conns: 10
  5. max_idle_conns: 10
  6. redis:
  7. host: "127.0.0.1"
  8. port: 6379
  9. password: ""
  10. db: 0 # use default DB
  11. pool_size: 100
  12. min_idle_conns: 5

定义结构体

在读取配置时,可以直接读取配置文件中的值,也可以将其映射到结构体中,方便我们统一管理及使用。

我们主要定义了三个结构体:

  • AppConfig: 应用配置,集中管理所有配置
  • MySQLConfig:MySQL配置
  • RedisConfig:Redis配置

具体定义如下:

// AppConfig my app config
type AppConfig struct {
    MySQLConfig `yaml:"mysql"`
    RedisConfig `yaml:"redis"`
}

// MySQLConfig mysql config
type MySQLConfig struct {
    DSN          string `yaml:"dsn"`            // write data source name.
    LogMode      bool   `yaml:"log_mode"`       // whether to open the log
    MaxOpenConns int    `yaml:"max_open_conns"` // max open conns
    MaxIdleConns int    `yaml:"max_idle_conns"` // max idle conns
}

// RedisConfig redis config
type RedisConfig struct {
    Host         string `yaml:"host"`
    Password     string `yaml:"password"`
    Port         int    `yaml:"port"`
    DB           int    `yaml:"db"`
    PoolSize     int    `yaml:"pool_size"`
    MinIdleConns int    `yaml:"min_idle_conns"`
}

读取配置并序列化到Go结构体中

我们使用 dubbo-go封装好的 yaml库来解析配置文件,并将其内容序列化到上述定义的结构体中:

package conf

import (
    "flag"

    "github.com/apache/dubbo-go/common/yaml"
)

const defaultConfigFile = "../conf/data.yml"

func Init() (config *AppConfig, err error) {
    var (
        b          []byte
        configFile string
    )
    // 1.Specify the configuration file using command line parameters
    flag.StringVar(&configFile, "dataConf", "../conf/data.yml", "choose conf file.")
    flag.Parse()

    if configFile == "" {
        configFile = defaultConfigFile
    }

    // 2.Load yml config
    if b, err = yaml.LoadYMLConfig(configFile); err != nil {
        return
    }

    // 3.Serialize the contents of the configuration file to struct
    config = new(AppConfig)
    if err = yaml.UnmarshalYML(b, config); err != nil {
        return
    }
    return
}

MySQL 初识化

我们使用 gorm 来操作MySQL,以下代码是初始化 gorm.DB

var (
    sqlDB *sql.DB
)

// InitMySQL init gorm.DB
func InitMySQL(cfg *conf.AppConfig) (db *gorm.DB, err error) {
    mysqlConf := mysql.Config{
        DSN:                       cfg.DSN, // DSN data source name
        DefaultStringSize:         256,     // string 类型字段的默认长度
        DisableDatetimePrecision:  true,    // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
        DontSupportRenameIndex:    true,    // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
        DontSupportRenameColumn:   true,    // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
        SkipInitializeWithVersion: false,   // 根据当前 MySQL 版本自动配置
    }
    gormConfig := isLogOn(cfg.LogMode)
    if db, err = gorm.Open(mysql.New(mysqlConf), gormConfig); err != nil {
        logger.Error("database:opens database failed", err)
        return
    }

    if sqlDB, err = db.DB(); err != nil {
        logger.Error("database:get DB() failed", err)
        return
    }
    // GORM 使用 database/sql 维护连接池
    sqlDB.SetMaxIdleConns(cfg.MaxIdleConns) // 设置空闲连接池中连接的最大数量
    sqlDB.SetMaxOpenConns(cfg.MaxOpenConns) // 设置打开数据库连接的最大数量
    return
}

// isLogOn gorm 日志配置,mod为true,表示开启日志
func isLogOn(mod bool) (c *gorm.Config) {
    if mod {
        c = &gorm.Config{
            Logger:                                   gormLog.Default.LogMode(gormLog.Info),
            DisableForeignKeyConstraintWhenMigrating: true,
            NamingStrategy: schema.NamingStrategy{
                // Whether the data table name is in plural form,default:false for plural table's name
                SingularTable: true,
            },
        }
    } else {
        c = &gorm.Config{
            Logger:                                   gormLog.Default.LogMode(gormLog.Silent),
            DisableForeignKeyConstraintWhenMigrating: true,
            NamingStrategy: schema.NamingStrategy{
                SingularTable: true,
            },
        }
    }
    return
}

Redis 初识化

我们使用 go-redis 来操作Redis,以下代码是初始化 redis.Client

var rdb *redis.Client

// Init init redis client
func InitRedis(cfg *conf.AppConfig) (*redis.Client, error) {
    rdb = redis.NewClient(&redis.Options{
        Addr:         fmt.Sprintf("%s:%d", cfg.RedisConfig.Host, cfg.RedisConfig.Port),
        Password:     cfg.Password,     // no password set
        DB:           cfg.DB,           // use default db
        PoolSize:     cfg.PoolSize,     // redis connection pool size
        MinIdleConns: cfg.MinIdleConns, // Set the minimum number of connections in the idle connection pool
    })
    if _, err := rdb.Ping().Result(); err != nil {
        logger.Error("redis ping failed", err)
        return nil, err
    }
    return rdb, nil
}

// Close close redis client
func Close() {
    _ = rdb.Close()
}

总结

main.go中调用了上述定义的方法以作验证,具体逻辑很简单。

dubbo-go启动时需要配置 server.ymllog.yml,这两个配置我们只需要配置即可,不需要我们手动读取并初始化。

项目开发中,除了MySQL\Redis,可能还会使用其他中间件,此时也可以仿照上述方式来读取并初始化自定义配置。