go-micro-config源码分析

June 28, 2020

go-micro/config基础特性及用法,参考官方文档

核心数据结构

感觉go-micro的设计理念应该是一切皆是Interface,所以除了Pluggable Sources等特性外,内部的一些数据结构上也预留了插件化的可能。几个数据结构在官网上都介绍比较清楚了,主要关注下几个核心数据结构的方法。

Config

暴露给用户的对象,用于加载、读取和同步数据源。

Loading...
  • 通过Load加载数据源(source)到config内部
  • 通过Watch返回的wather观察数据更新
  • Sync写回给数据源

虽然gomicro中一切皆Interface,但内部实现依赖DefaultConfig。Config的实现依赖两个核心数据结构:

Loading...

Loader负责加载Source、管理watcher链表、管理sets数据、执行Merge操作等,为Config屏蔽了不少下层逻辑,使对外的接口尽可能干净。

Reader就比较好理解了,参见官方文档。

注意Config中存在一个唯一的goroutine,用来聚合各个Source的变更。每个Source在Loader中也维护了一个goroutine,详见后面Loader部分。

Loading...

Source

配置内容源,支持以下格式:

  • configmap - k8s configmap
  • consul - consul
  • etcd - etcd v3
  • env - 环境变量
  • file - 配置文件
  • flag - 命令行标识
  • grpc - grpc
  • memory - 内存...
Loading...

Watcher的底层实现还是依赖各Source的watcher,比如file source,就引入了"fsnotify/fsnotify"来监控文件的变化。每个Source被Loader Load时会生成一个对应的source watcher goroutine,用于监控source的变更。

各种Source的实现就没有具体关注了。

Loader

用于加载Source,实现的核心数据结构。

Loading...

框架中提供了一个缺省的memory loader,Loader的存在主要基于以下几个原因:

1、Source是可以聚合(Merge)的,在Reader中提供了Merge的实现。当系统存在多份Source时,需要将Source聚合好之后给到Config,否则Config中就得处理此类逻辑。

2、对Source的failover。当Load失败时,提供了Snapshot能力。

Loader还维护了核心的数据结构:watcher列表。source watcher goroutine发现内容变更时会将变更的内容通知给所有goroutine(通过Get读取对应的path值),“通知”只是将内容写到了update 队列。具体的变更逻辑还得外部调用方调用Next感知。

注意调用方增加Watcher时,Loader只是在watcher list中增加一个节点,没有goroutine的开销。但是Suorce的内容变更会通知所有wathcher,wather会根据内容的变化情况(bytes.Equal)来决定是否通知上层变更。

Loading...

另上面也提供,config中也维护了一个gouroutine,watch的path是各个source的聚合。当一个Source的内容变更时需要将各个Source内容重新聚合后返回给config,以更新Config中的snapshot和value值。

总结

图中画得比较明白了,比较难以理解的地方也在于Loader为支持多个Source的一些操作,包括sets列表、Merge操作。当理解需要支持多个Source的聚合之后就比较好理解了。

整体感觉config的封装过于复杂了,包括的viper的引入还进行了改造,在代码实现中有比较多的类似的逻辑,包括命名也有不少重复,阅读起来有点心累。估计代码圈复杂度比较高。。

See all postsSee all posts