kitex
kitex copied to clipboard
Proposal: Kitex 对接远程配置中心的方案
目标
多数据源
定义标准化的接口来适配不同的数据源。 数据源可以是环境变量,本地文件,这两个由框架内置,也可以是常见的配置中心,例如 etcd、nacos 和 apollo 等,会由 kitex-contrib 来提供。 如果有特殊需求,可以通过接口实现来适配。 支持一次性从多个数据源加载数据,key 相同的配置将会被覆盖,具体的优先级由配置源实现中的读取顺序来决定。
多格式
支持多种格式的数据解析,内置常见的 json 和 yaml,如果用户有需要可以实现对应的 Decoder 接口。
动态加载
在应用启动阶段会首次加载所需配置,并在后台监听配置的变更,自动重新加载最新配置,服务不需要重启便能更新配置。
可观察性
能够观察到当前正在使用的配置,以及配置的每次变更事件,以便排查问题。
接口定义
// Loader is the entry of configuration.
type Loader interface {
Load() Config
}
// Parser parse the raw data from configCenter to readable config.
type Parser interface {
Parse(data []byte) (map[string]interface{}, error)
String() string
}
// Config is configuration been stored in dynamic config center.
type Config interface {
Equal(Config) bool
}
// Provider provide the raw data of config.
type Provider interface {
Load() ([]byte, error)
Watch() Watcher
}
// EventHandler define all event handlers about config change.
type EventHandler interface {
OnAdd(key string, val interface{})
OnUpdate(key string, oldVal, newVal interface{})
OnDelete(key string)
}
// Watcher watch and handle the change events.
type Watcher interface {
Watch(e EventHandler)
}
// Dumper dump cached data.
type Dumper interface {
Dump() interface{}
}
- Loader:它通过一个 key 来获取配置 Config。它是 Provider、Parser 的使用者,通过 Provider 获取到配置的 raw data,然后通过 Parser 完成配置内容的解析。Loader 还通过 Provider 返回的 Watcher 来获取配置变更相关的通知事件,并重新加载配置 Config;
- Provider:它是配置的提供者,可以是本地文件,也可以远程的配置中心,如 k8s configmap,etcd等。Provider 能够感知到配置的变更,并给 Loader 发出通知,所以 Provider 需要返回一个 Watcher 给 Loader;
- Watcher:配置信息是在初次加载后,当发生变化时希望框架能感知到并使用加载最新配置。Watcher 的作用就是监听配置的变更事件并及时通知框架,如本地文件的话可以通过定时检查文件的 mtime 是否发生变化来确认变更;
- EventHandler:Watcher 的所有事件 handler,每种类型的事件都有对应的 handler 来处理;
- Parser:Loader 拿到配置数据后,还需要通过合适的解析器将数据解析成某种结构化的配置信息,如JSON、YAML等;
- Config:不管是哪种类型的配置信息,都应该是可对比的,因为很多时候需要靠对比来判断是否有变更;
- Dumper:用于把缓存中的配置 dump 下来,类似于 snapshot 用于观察当前正在使用的配置;
如何使用
初始化
从本地文件加载
import (
"github.com/cloudwego/kitex/config"
"github.com/cloudwego/kitex/config/file"
)
path := "conf/config.yaml"
c := config.NewProvider(
config.WithSource(
file.NewSource(path),
)
)
从外部配置中心加载,例如 consul
import (
"github.com/cloudwego/kitex-contrib/consul"
)
consulClient, err := api.NewClient(&api.Config{
Address: "127.0.0.1:8500",
})
if err != nil {
panic(err)
}
cs, err := consul.New(consulClient, consul.WithPath("app/kitex/configs/"))
if err != nil {
panic(err)
}
c := config.NewProvider(config.WithSource(cs))
不同的配置源使用方式大致相同,需要参考各自的文档或 example。
读取配置
在初始化的时候,需要指定 Parser 用于将配置内容反序列化解析出来
import (
"gopkg.in/yaml.v2"
"github.com/cloudwego/kitex/config/parser/yaml"
)
path := "conf/config.yaml"
c := config.NewProvider(
config.WithSource(file.NewSource(path)),
config.WithParser(yaml.NewParser()),
)
Provider 通过 Load 拿到 Config,再通过 Parser 会返回一个 map,通过 key 来获取对应的 value
监听变更
Provider 里面的 Watch 方法会返回一个 Watcher,会监听某个 key 的 Add、Update 和 Delete 事件,在本地或配置中心的配置有变更时,执行自定义的回调函数进行处理。
数据快照
提供了 Dump 接口,给 debug 端口使用,可以随时观察到当前缓存中的数据快照,方便排查问题
框架集成
Config 有两种使用方,一个是框架自身用于获取服务治理相关的配置,例如超时设置和熔断等,另一个则是用户用来获取一些业务相关的配置。 为了只维护一份缓存,框架会提供一个 configManager,configManager 里面会管理所有的 cofing。 由于服务治理的配置需要在服务启动前或发起调用前就需要获取,然而框架并不知道需要加载哪些类型的配置,因此需要用户实现 fetchFunc,用于 configManager 能够及时加载相关配置。
// ConfigurationManager provides access to get config, and manages configurator's lifecycle.
type ConfigurationManager interface {
Get(key, namespace string) interface{}
}
// ServiceMeta describe metas about service.
type ServiceMeta interface {
Name() string
Type() ServiceType
Tags() map[string]string // cluster, method, etc
}
// ManagerOption set values of ManagerOptions.
type ManagerOption func(*ManagerOptions)
// ManagerOptions contains settings of ConfigurationManager.
type ManagerOptions struct {
// serviceHandler register callers and callees, it's used to obtain the configuration in advanced.
serviceHandler func(callers, callees serviceMeta ServiceMeta) error
// fetchFunc define the func which fetch remote configuration from dynamic config center.
fetchFunc func(configLoader Loader, callers, callees []ServiceMeta) (map[string]Config, error)
}
FAQ
- Q:如何区分框架内服务治理的配置和用户自定义配置? A:通过类似 namespace 或 group 的概念来区分,主流的配置中心都有对应的概念。框架内置的服务治理配置 namespace 约定为 kitex_remote_config。
- Q:如何扩展对接不同的配置中心? A:在 kitex-contrib 中提供对应实现,例如 etcd、consul 等
- Q:给个具体的使用 example A:后续会在 kitex-example 中提供
- 治理配置约定的 Key 有哪些需要明确
- 不同配置源的优先级需要明确
- 业务配置和治理配置如果进行区分,可以给定具体的前缀,也要参考下后续 hertz 的情况
治理配置的加载在 #973 中跟进。