使用配置文件启动go项目

在go的项目中,运行配置经常需要修改的,比如端口号,地址,各种变动的token,这些值都是随时可能根据需求变动的。如果写死在程序里,要修改的时候会很不方便。
同时,一些敏感的数据比如各种用户名,或者密码,也需要集中存放,便于查询也便于保护。

这一般都通过配置文件来实现,json或者yaml。本人比较喜欢使用yaml的配置,比较美观易读。
不重要的配置数据可以直接在配置文件中明写,敏感的可以写密文,然后把密钥写环境变量中。

下面就来说一下如何在一个go项目中使用配置文件来启动项目。

对于一些典型的示例代码,都是直接在main函数中完成的,但是实际中,我们的项目会启动多种服务,比如一个简单的API后端,可能就会使用Gin框架,使用数据库,使用redis等,启动多个服务就会涉及到各个服务的参数怎么设置。
如果每个都在相应服务的内部申明,这显然是不科学的,把配置集中设置才能更有效的管理服务。

我们可以在项目最外层(和main函数同级)设置一个config文件夹,里面存放一个app.yaml文件和一个config.go文件。

前者存放配置,后者写一个解析配置的函数,用于读取配置。
我写一个示例的文件:

server:
  env: "debug"
  address: "localhost"
  port: 3000

db:
  port: 3306
  host: "localhost"
  name: "digbgm"
  user: "root"
  password: "xxxxxxxxxx"
  migrate: true

redis:
  enable: true
  port: 6379
  host: "localhost"
  password: "xxxxxx"

之后在config.go文件中定义配置文件的结构体,以及一个函数,使用go提供的一个yaml包对yaml文件读取:

package config

import (
	"os"

	"gopkg.in/yaml.v3"
)

type Config struct {
	Server ServerConfig `yaml:"server"`
	DB     DBConfig     `yaml:"db"`
	Redis  RedisConfig  `yaml:"redis"`
}

type ServerConfig struct {
	Env     string `yaml:"env"`
	Address string `yaml:"address"`
	Port    int    `yaml:"port"`
}

type DBConfig struct {
	Host     string `yaml:"host"`
	Port     int    `yaml:"port"`
	Name     string `yaml:"name"`
	User     string `yaml:"user"`
	Password string `yaml:"password"`
	Migrate  bool   `yaml:"migrate"`
}

type RedisConfig struct {
	Enable   bool   `yaml:"enable"`
	Host     string `yaml:"host"`
	Port     int    `yaml:"port"`
	Password string `yaml:"password"`
}

// 读取配置文件 app.yaml
func ConfigParse(appConfig *string) (*Config, error) {
	config := &Config{}

	file, err := os.Open(*appConfig)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	if err := yaml.NewDecoder(file).Decode(&config); err != nil {
		return nil, err
	}

	return config, nil
}

完成后我们就可以通过调用该函数的方式读取配置文件,从而将这些值传递给需要的对象。

我们在main函数中可以使用flag包来加载yaml文件

var (
	port    string
	ginmode string
)

func main() {
	appConfig := flag.String("config", "config/app.yaml", "application config path")
	conf, _ := config.ConfigParse(appConfig)
	if conf != nil {
		port = fmt.Sprint(conf.Server.Port)
		ginmode = conf.Server.Env
	} else {
		log.Println("Error:config file is nil")
	}
     // ... ...

这样我们就可以顺利通过配置文件来读取端口号以及gin的运行模式这两个参数了。

同时,main中得到的conf的值也可以作为参数,传递给其他需要配置文件的地方,但是注意,conf最好作为指针传递。

//其他包中定义一个获取配置的函数
func NewWxConfig(conf *config.WxConfig) {
	wxToken = conf.WxToken
	appID = conf.AppID
	appSecret = conf.AppSecret
}

... ...

util.NewWxConfig(&conf.Wx) //传入conf的地址,得到配置文件中的参数

实际应用

使用配置文件作为启动参数,这让我们的main函数变的很简练。
如果我们使用了GIN框架创建了一个后端,监听3000端口,我们通常会在main函数中使用:

	gin.SetMode(ginmode)
	router := gin.Default()

	router.GET("/", handle1)
	router.POST("/",handle2)

	log.Fatalln(router.Run(":" + port))

这样来创建一个gin服务。

但是如果我们有多个服务要创建,都挤在一个main函数中是不合理的,所以我们可以新建一个server.go文件,用来读取配置文件,启动多种服务,而main只需要调用server提供的接口就可以了。

server函数中需要面向对象的思想,新建一个server的结构体,我做个示例:

type Server struct {
	engine *gin.Engine
	config *config.Config
	db     *gorm.DB
	rdb    *redis.Client
}

写一个new函数,用来读取配置,并将配置信息分发给各个服务:

func New(conf *config.Config) (*Server, error) {
	if conf != nil {
		var err error
		db, err = database.Newdb(&conf.DB)
		if err != nil {
			return nil, errors.Wrap(err, "db init failed")
		}
		rdb, err = database.NewRedisClient(&conf.Redis)
		if err != nil {
			return nil, errors.Wrap(err, "redis client failed")
		}
	} else {
		log.Println("Error:config file is nil")
	}
	// 自动迁移模型
	if conf.DB.Migrate {
		db.AutoMigrate(&database.User{})
	}
	//设置Gin的模式
	gin.SetMode(conf.Server.Env)
	// 创建一个 Gin 引擎
	r := gin.Default()
	return &Server{
		engine: r,
		config: conf,
		db:     db,
		rdb:    rdb,
	}, nil
}

其中database.Newdbdatabase.NewRedisClient是数据库中接受配置信息的函数,传入后即可加载服务了。

然后再给server一个run的方法,用于启动主服务–Gin:

func (s *Server) Run() error {
	s.initRouter()

	//读取服务器地址
	addr := fmt.Sprintf("%s:%d", s.config.Server.Address, s.config.Server.Port)

	// 启动Gin服务器
	err := s.engine.Run(addr)
	if err != nil {
		fmt.Println("Failed to start Gin server")
	}
	return err
}
func (s *Server) initRouter() {
	r := s.engine

	// 设置路由
	r.GET("/", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Hello, Gin!",
		})
	})
}

在main函数中读取配置s, err := server.New(conf),然后调用s.Run(),就可以将配置文件作为启动参数加载到项目中了

注意点

请注意yaml文件的格式,只能用空格,不能用tab,并且冒号后要有一个空格,yaml的严格缩进需要仔细填写,否则很容易在读取yaml出现空指针报错。请自行查看yaml文件的规范,或者参考已有示例。

你可能感兴趣的:(golang,开发语言,后端)