目录
- 配置分类
- 最佳实际
- 配置管理
- 参考链接
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}
自定义database
和timeout
,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