本文主要记录kratos项目配置的定义、读取和源码的学习
at first
服务在启动的基本都会用到配置文件,那么如果是你来写config工具库,它所能提供的功能是什么?
- 读取不同场景的配置
- 读取不同格式的配置
- 支持热更
- 支持拓展,提供自定义实现
kratos/config 有那些功能
- kratos/config包支持多种配置源,包括
- 本地文件
- 本地环境
- contrib/config支持配置中心
- kratos/config 支持多种配置格式
- json
- proto
- xml
- yaml
- 支持热更
- 支持其它格式的配置文件
定义配置
在项目目录下创建文件: workspace/configs/config.yaml
service:
name: config
version: v1.0.0
http:
server:
address: 0.0.0.0:8000
timeout: 1s
grpc:
server:
address: 0.0.0.0:9000
timeout: 1s
加载配置文件
WithSource
使用withsoure指定数据源为本地文件
package main
import (
"flag"
"log"
"github.com/go-kratos/kratos/v2/config"
"github.com/go-kratos/kratos/v2/config/file"
)
var flagconf string
func init() {
flag.StringVar(&flagconf, "conf", "config.yaml", "config path, eg: -conf config.yaml")
}
func main() {
flag.Parse()
c := config.New(
config.WithSource(
file.NewSource(flagconf),
),
)
if err := c.Load(); err != nil {
panic(err)
}
//定义配置JSON字段
var v struct {
Service struct {
Name string `json:"name"`
Version string `json:"version"`
} `json:"service"`
}
//将配置Unmarshal 到struct
if err := c.Scan(&v); err != nil {
panic(err)
}
log.Printf("config: %+v", v)
//获取与该键关联的值
name, err := c.Value("service.name").String()
if err != nil {
panic(err)
}
log.Printf("service: %s", name)
// 热更执行钩子
if err := c.Watch("service.name", func(key string, value config.Value) {
log.Printf("config changed: %s = %v\n", key, value)
}); err != nil {
panic(err)
}
<-make(chan struct{})
}
output:
$ config: {Service:{Name:config Version:v1.0.0}}
$ service: config
WithDecoder
Decoder用于将配置文件内容用特定的反序列化方法解析出来
c := config.New(
config.WithSource(
file.NewSource(flagconf),
),
config.WithDecoder(func(src *config.KeyValue, target map[string]interface{}) error {
if src.Format == "" {
// expand key "aaa.bbb" into map[aaa]map[bbb]interface{}
keys := strings.Split(src.Key, ".")
for i, k := range keys {
if i == len(keys)-1 {
target[k] = src.Value
} else {
sub := make(map[string]interface{})
target[k] = sub
target = sub
}
}
return nil
}
if codec := encoding.GetCodec(src.Format); codec != nil {
return codec.Unmarshal(src.Value, &target)
}
return fmt.Errorf("unsupported key: %s format: %s", src.Key, src.Format)
}),
)
output 和上例一致
原理
// Config is a config interface.
type Config interface {
Load() error
Scan(v interface{}) error
Value(key string) Value
Watch(key string, o Observer) error
Close() error
}
- 指定数据源的这样会将整个目录中的所有文件进行解析加载,合并到同一个map中
c := config.New(
config.WithSource(
file.NewSource(flagconf),
),
)
if err := c.Load(); err != nil {
panic(err)
}
- 使用之前创建好的config实例,调用.Scan方法,读取配置文件的内容到结构体中,这种方式适用于完整获取整个配置文件的内容
if err := c.Scan(&v); err != nil {
panic(err)
}
fmt.Printf("config: %+v", v)
- New的时候会有一个默认的 decoder
func New(opts ...Option) Config {
o := options{
logger: log.DefaultLogger,
decoder: defaultDecoder,
resolver: defaultResolver,
}
for _, opt := range opts {
opt(&o)
}
return &config{
opts: o,
reader: newReader(o),
log: log.NewHelper(o.logger),
}
}
通过option可以拓展自定义 decoder
func WithDecoder(d Decoder) Option {
return func(o *options) {
o.decoder = d
}
}