配置管理
最佳实践思想
- 区分必选值与可选择
- 配置文件与选项解耦
- 避免复杂
- 简单化, 除了必选的值, 默认值应该是科学的, 合理的, 最佳实践的值
- 防御编程, 防止其他人乱改和运行时修改配置, 如果用户手一抖, 一秒钟填写成1分钟, 对不合理的配置直接panic
- 配置版本和应用对齐, 回滚应用时, 应用和配置文件应该一一对应, 一起回滚, 在配置中心或者k8s配置
最佳实践
proto + 配置语言(json/yaml/toml)
proto可以进行高亮 yaml编写配置就可以与proto进行解耦
演进
函数配置
防止在运行时修改DailOption配置
优点:
- 告诉你可选和必选值
- 函数内方便使用默认值
扩展:
如果要扩展, 必须新增函数, 例如
DailDatabase等才可以扩展
type DailOption struct { // dailOption, 小写, 意味着没有任何人有修改里面的值 f func(*dailOption)}
func Dial(network, address string, options ...DailOption) (Conn, error){ // 默认值 do := dailOption { dial: net.Dial, } // 如果没有传递options, 也不会造成影响 for _, option := range options { option.f(&do) }}或者:
type DailOption func(*dailOption)
func Dial(network, address string, options ...DailOption) (Conn, error){ do := dailOption { dial: net.Dial, } for _, option := range options { option(&do) }}函数配置, 包含返回值
用于单元测试等, 需要切换参数配置时 改之前与改之后
type option func(f *Foo) optionfunc Verbosity(v int) option { return func(f *Foo) option { prev := f.verbostiy f.verbostiy = v return Verbosity(prev) }}
func DoSomethingVerbosity(foo *Foo, verbosity int) { prev := foo.Option(pkg.Verbosity(verbostiy)) defer foo.Option(prev)}包含interface的函数配置
type GreeterClient interface {SayHello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*SayHelloReply, error)}
type CallOption interface { before(*callInfo) error after(*callInfo)}// EmptyCallOption 不改变 CallCallOptiontype EmptyCallOption struct{}
// 对原有的grpc.CallOption进行扩展type TimeoutCallOption struct { grpc.EmptyCallOption Timeout time.Duration}其它配置的缺点
传统结构体(不加指针)
缺点: 不加指针是copy该结构体, 无法使用默认值, 以及0值和设定的值无法区分
func Dail(c Config) (cn Conn, err error)配置文件解析
如果是把配置文件解析到go结构体, 那么会产生以下问题:
例如Redis, 如果你使用了配置文件映射到结构体, 那么如果别人使用的是redis.NewConn, 与你的配置文件冲突, 那么副作用是什么?
server: address: xxx
// structtype Config struct { Address string}
func main() { c := Config {Address: xxx} // 正常使用 c := Redis.Config { Address: c.Address } // 其它使用 r, _ := &redis.NewConn(c) c.Addr = "xxx" // 副作用是什么?}参数扩展
这样写不知道有多少个的方法
func Dial(network, address, timeout string)func DialDatabase(network, address, timeout, database string) (Conn, error)函数可选参数
缺点: 参数不清晰, 如果填多个Config, 哪个生效呢? 有什么副作用?
func Dail(c ...Config) (cn Conn, err error)指针默认配置
缺点: 使用nil来表示使用默认的值, 这个nil作为默认值不是特别好. nil不应该作为公共函数的参数
func Dail(c *Config) (cn Conn, err error)
func main() { Dail(nil)}