目录

  • 配置分类
  • 最佳实际
  • 配置管理
  • 参考链接

1. 配置分类

环境配置

利用在部署的时候应该确定的信息,这些信息不应该写在配置文件或者配置核心,而是应该由部署平台在利用启动的时候注入,例如k8s间接在容器启动的时候注入

动态配置

利用资源初始化的时候须要的信息,如服务的根本信息、Mysql、Redis、Mongodb等配置信息,个别是放在我的项目的根目录下的动态文件

动静配置

应用程序在运行是的时候须要的一些配置,相似某些性能开关,个别是在治理后盾去管制

2. 最佳实际

2.1 形式一

json动态文件治理配置文件配置

{  "server": {    "addr": "0.0.0.0:8000"  },  "mysql": {    "driver": "mysql",    "dsn": "root:@tcp(127.0.0.1:3306)/testdb?parseTime=True"  }}

我的项目依赖全局的Config map

package mainimport (   "database/sql"   "encoding/json"   "net/http"   "os")var Config = make(map[string]map[string]string)func main() {   file, err := os.Open("/path/to/config/config.json")   if err != nil {      panic(err)   }   decoder := json.NewDecoder(file)   err = decoder.Decode(&Config)   if err != nil {      panic(err)   }     defer file.Close()     db, err := NewMysql(Config["mysql"]["driver"], Config["mysql"]["driver"])   if err != nil {      panic(err)   }   server := &http.Server{      Addr: Config["server"]["addr"],   }   err = server.ListenAndServe()   if err != nil {      panic(err)   }}// 连贯mysqlfunc NewMysql(driver, dsn string) (*sql.DB, error) {   return sql.Open(driver, dsn)}

2.2 形式二

创立一个Config构造体类型,业务依赖这个构造体,并且将json配置解析到这个构造体

import (   "database/sql"   "encoding/json"   "net/http"   "os")var Config Cfunc main() {   file, err := os.Open("/path/to/config/config.json")   if err != nil {      panic(err)   }   decoder := json.NewDecoder(file)   err = decoder.Decode(&Config)   if err != nil {      panic(err)   }   defer file.Close()      db, err := NewMysql(Config.Mysql)   if err != nil {      panic(err)   }   server := &http.Server{      Addr: Config.Server.Addr,   }   err = server.ListenAndServe()   if err != nil {      panic(err)   }}type C struct {   Server *Server   Mysql  *Mysql}type Server struct {   Addr string `json:"addr"`}type Mysql struct {   Driver string `json:"driver"`   Dsn    string `json:"dsn"`}func NewMysql(mysql *Mysql) (*sql.DB, error) {   return sql.Open(mysql.Driver, mysql.Dsn)}

3. 配置管理

Redis Server 配置

// Server defines options for redis cluster.type Server struct {    Addr         string    Password     string    Database     int    DialTimeout  time.Duration     }
  • 必填项 不能为空

    • 地址Addr
    • 明码 Password
  • 选填项 能够为空,有默认值

    • 数据库Database
    • 超时工夫Timeout

针对下面的配置,有多类形式去结构

3.1 形式一:多种函数签名

创立一个通用的结构器

func NewServer(addr string, password string) (*Server, error) {    return &Server{addr, password, 0, time.Second}, nil}

自定义databasetimeout,Go不反对函数重载,用不同的函数签名去实现对应的配置

func NewServerWithDatabase(addr string, password string,database int)(*Server, error){    return &Server{addr, password, database, time.Second}, nil}func NewServerWithTimeout(addr string, password string,timeout time.Duration)(*Server, error){    return &Server{addr, password, 0, timeout}, nil}

弊病 :

  • 如果咱们要自定义更多的需要之后,岂不是要针对每一种需要都要新增一个函数签名
  • 函数参数会十分长,不利于扩大
  • 不是优雅的姿态

3.2 形式二:传递配置构造体

传递一个配置的构造体,把选填项都放到这个构造体中,解决参数比拟长和各种自定义需要

type Server struct {    Addr     string    Password string    Option   *Option}type Option struct {    Database    int    DialTimeout time.Duration}
func NewServer(addr string, password string, option *Option) (*Server, error) {    return &Server{addr, password, option}, nil}func NewServer(addr string, password string,option *Option) (*Server, error) {    return &Server{addr, password, option}, nil}

调用

NewServer("127.0.0.1:6379", "password", &Option{Database: 1, DialTimeout: time.Second})

弊病 :

  • 外部应用的时候,须要先判断指针是否为nil
  • 因为传递的是一个指针,能够在函数外边进行批改,这不是预期内的行为,会产生不可预知的事件
  • 没方法辨别默认值

3.3 形式三:Builder模式

// ServerBuilder 应用一个builder类来做包装type ServerBuilder struct {   Server   Err error}func (s *ServerBuilder) WithAddr(addr string) *ServerBuilder {   s.Server.Addr = addr   return s}func (s *ServerBuilder) WithPassword(pwd string) *ServerBuilder {   s.Server.Password = pwd   return s}func (s *ServerBuilder) WithTimeout(tw time.Duration) *ServerBuilder {   s.Server.Option.DialTimeout = tw   return s}func (s *ServerBuilder) WithDatabase(db int) *ServerBuilder {   s.Server.Option.Database = db   return s}func (s *ServerBuilder) Build() Server {   return s.Server}

调用

builder := ServerBuilder{}builder.WithAddr("127.0.0.1:6379").WithPassword("pwd").WithTimeout(2 * time.Second).WithDatabase(8).Build()

弊病 :

  • 须要新增构造体来包装
  • 如果不新增构造体在Server上间接build的话,处理错误的时候要新增error成员,不纯净

3.4 形式四:Function Optional

定义一个函数类型

type Opt func(option *Option)

定义相干函数

func WithDatabase(database int) Opt {    return func(option *Option) {        option.Database = database    }}func WithTimeout(to time.Duration) Opt {    return func(option *Option) {        option.DialTimeout = to    }}func NewServer(addr string, password string, opts ...Opt) (*Server, error) {    option := &Option{        Database:    0,        DialTimeout: time.Second,    }    for _, opt := range opts {        opt(option)    }    return &Server{        Addr: addr,        Password: password,        Option:option,    },nil}

调用

NewServer("127.0.0.1:6379", "password", WithTimeout(2*time.Second), WithDatabase(8))

劣势:

  • 程序无关
  • 可维护性和扩展性更好
  • 更直观,应用老本更低
  • 没有什么令人困惑的事(是nil 还是空)

4. 参考链接

  • functional-options-for-friendly-apis
  • Go 编程模式:Functional Options
  • Functional Options in Go
  • 怎么应用viper治理配置
  • viper