开发者

go kratos源码及配置解析

目录
  • 相关类图
    • 流程解释
  • 代码案例

    相关类图

    项目启动时一般前置条件为解析配置文件, 我www.devze.com们看下这块是怎么设计的.

    go kratos源码及配置解析

    流程解释

    • config 实现 config Interface 接口, 初始化reader对象
    • 调用Load方法, 通过入口注入的不同文件源, 调用实现了source interface 对应的Load方法(以file举例子)
    • file 通过 Load 入口, 判断是目录还是文件, 执行对应的方法, 拿到文件内容返回
    • config 拿到file返回的内容, 编程客栈交给reader去合并
    • reader 根据文件格式(yml) 转为 map[string]interface{} 结构, 并进行覆盖合并, 将结果存到对象变量上
    • reader 处理完成后, config调用 file watcher 进行文件变更监听, 启动协程监听

    由此可见, config对象其实作为入口, 将数据生产交给file、env, 将数据加工解析交给 reader

    代码案例

    init 接收外部参数定义解析配置文件地址

    func init() {
    	flag.StringVar(&commonconf, "common", "./configs/local", "common config path, eg: -conf config.yaml")
    	flag.StringVar(&flagconf, "conf", "./configs/local/api", "config path, eg: -conf config.yaml")
        flag.Parse()
    }
    

    使用config.New初始化文件配置

    //初始化配置
    //新增两个配置源, 文件格式 coivuaZSzynmmon and flagconf 路径
    c := config.New(
        config.WithSource(
            file.NewSource(commonconf),
            file.NewSource(flagconf),
        ),
    )
    // 关闭watch相关的监听器
    defer c.Close()
    // 加载配置文件
    if err := c.Load(); err != nil {
    	panic(err)
    }
    //解析配置到bc结构上
    var bc conf.Bootstrap
    if err := c.Scan(&bc); err != nil {
        panic(err)
    }
    

    我们看下config.New的实现

    // 初始化解析器, 关联reader对象, 数据交给由reader加工和存储
    func New(opts ...Option) Config {
    	o := options{
    		decoder:  defaultDecoder,
    		resolver: defaultResolver,
    	}
    	for _, opt := range opts {
    		opt(&o)
    	}
    	return &config{
    		opts:   o,
    		reader: newReader(o),
    	}
    }
    

    Options 有下面几个属性

    type options struct {
    	sources  []Source // 配置源, 由初始化负责传入的source配置源
    	decoder  Decoder //  解析器
    	resolver Resolver // 变量解析替换
    }
    

    执行 Load 配置加载

    func (c *config) Load() error {
        // 因为我们在入口传入的是file对象, 所以执行src load的时候也是file对象的 load 方法
    	for _, src := range c.opts.sources {
            // 获取文件内容(可能是一个目录, 会存在多个文件)
    		kvs, err := 开发者_JS培训src.Load()
    		if err != nil {
    			return err
    		}
    		for _, v := range kvs {
    			log.Debugf("config loaded: %s format: %s", v.Key, v.Format)
    		}
            // 合并配置key
    		if err = c.reader.Merge(kvs...); err != nil {
    			log.Errorf("failed to merge config source: %v", err)
    			return err
    		}
            // 调用file watch, 监听文件变化
    		w, err := src.Watch()
    		if err != nil {
    			log.Errorf("failed to watch config source: %v", err)
    			return err
    		}
    		c.watchers = append(c.watchers, w)
            // 异python步监听文件变化(调用对应的watch对象)
    		go c.watch(w)
    	}
        // 解析内容中是否包含 ${APPID:default} 变量
        // 如果在配置文件中存在 APPID: xx 配置, 则进行替换
        // 否则使用default默认值
    	if err := c.reader.Resolve(); err != nil {
    		log.Errorf("failed to resolve config source: %v", err)
    		return err
    	}
    	return nil
    }
    

    异步watch

    func (c *config) watch(w Watcher) {
    	for {
    		kvs, err := w.Next()android
    		if err != nil {
    			if errors.Is(err, context.Canceled) {
    				log.Infof("watcher's ctx cancel : %v", err)
    				return
    			}
    			time.Sleep(time.Second)
    			log.Errorf("failed to watch next config: %v", err)
    			continue
    		}
        	// 处理逻辑忽略 .....
    		c.cached.Range(func(key, value interface{}) bool {
    			k := key.(string)
    			v := value.(Value)
    			if n, ok := c.reader.Value(k); ok && reflect.TypeOf(n.Load()) == reflect.TypeOf(v.Load()) && !reflect.DeepEqual(n.Load(), v.Load()) {
    				v.Store(n.Load())
    				if o, ok := c.observers.Load(k); ok {
    					o.(Observer)(k, v)
    				}
    			}
    			return true
    		})
    	}
    }
    

    Scan 将配置转换成结构体

    var bc conf.Bootstrap
    if err := c.Scan(&bc); err != nil {
        panic(err)
    }

    以上就是go kratos源码及配置解析的详细内容,更多关于kratos源码配置的资料请关注我们其它相关文章!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜