前言
由于使用kratos有一段时间了,期间也遇到过问题,但都顺利解决,趁着机会把源码通读一番,做个记录。
首先阅读文章之前先通读 官方文档 有个大概的认识,最好能动手操作,写个demo。
一、Kratos Run 命令启动程序
kratos项目在开发过程中一般使用kratos run的命令,将服务启动起来,先来看看这部分代码的实现过程。
命令相关的代码在cmd/kratos/main目录下,使用到的命令行库是 cobra 和 survey
。而run命令实现是在cmd/kratos/internal/run下面。
整体思路如下:
- 匹配输入的命令行参数,获取工作目录
- 如果工作目录为空,则获取cmd开头的文件夹下所包含的可能的服务目录(serviceDirs)。
- serviceDirs长度为空报错,为1就取该项,大于1则将服务目录列表显示到命令行中,供用户选择要执行的命令
- 用户选择后,利用exec.Command执行go run dir args…命令启动服务。
- 结束。
二、Kratos 中配置读取的方式
我们在写项目时,第一步就是配置的加载,微服务或者说云原生应用的配置最佳实践是将配置文件和应用代码分开管理——不将配置文件放入代码仓库,也不打包进容器镜像,而是在服务运行时,把配置文件挂载进去或者直接从配置中心加载。Kratos的config组件就是用来帮助应用从各种配置源加载配置。并且支持多种配置源,多种配置格式,热更新,配置合并等。代码实现主要在 /config下面,接下来看看每一模块如何实现的。
1. 多种配置源的支持
Kratos定义了标准化的Source和Watcher接口来适配各种配置源。
// KeyValue is config key value.
type KeyValue struct {
Key string
Value []byte
Format string
}
// Source is config source.
type Source interface {
Load() ([]*KeyValue, error)
Watch() (Watcher, error)
}
// Watcher watches a source for changes.
type Watcher interface {
Next() ([]*KeyValue, error)
Stop() error
}
框架内置本地文件和环境变量env的实现。另外也提供了常用的配置中心的适配方式(etcd,kubernetes等)我们仅分析下本地文件的方式,总体思路都是实现Load接口,将数据读取到KeyValue结构体中的Value字段。
定义file结构体实现了Load和Watch接口方法。其中Load用来加载数据,如何提供的路径是文件则调用loadFile方法,将文件内容,文件名称,文件扩展加载到KeyValue结构体中。如果路径是一个目录,调用loadDir方法遍历目录下的文件内部依旧调用loadFile拿到KeyValue数组。
func (f *file) Load() (kvs []*config.KeyValue, err error) {
fi, err := os.Stat(f.path)
if err != nil {
return nil, err
}
if fi.IsDir() {
return f.loadDir(f.path)
}
kv, err := f.loadFile(f.path)
if err != nil {
return nil, err
}
return []*config.KeyValue{kv}, nil
}
2. 多种配置格式
多种配置格式的实现在 encoding 中,支持json,proto,yaml等,我们也可以通过Codec 实现自己的文件格式。
type Codec interface {
// Marshal returns the wire format of v.
Marshal(v interface{}) ([]byte, error)
// Unmarshal parses the wire format into v.
Unmarshal(data []byte, v interface{}) error
// Name returns the name of the Codec implementation. The returned string
// will be used as part of content type in transmission. The result must be
// static; the result cannot change between calls.
Name() string
}
以最常用的json为例,首先定义一个结构体,实现上述接口,然后调用encoding.RegisterCodec将结构体对象注册到编码map集合中即可。源码中除了复用json的序列化方式同时也新增proto msg数据到json的转换。其他格式实现方式类似,yaml复用gopkg.in/yaml.v3库中的序列化和反序列化方法,proto复用protobuf/proto库中的方法。从而实现所有的数据格式统一到一个接口中,方便扩展和维护。
3. 热更新
热更新是通过监听文件变化收到通知后,对配置进行处理。文件变化利用fsnotify库的实现
4. 配置合并
所有的key都是在一个字典中的,读取到的相同的key后面的会覆盖前面,所以尽量使用不同的层级关系或者名称。
总结
本文简单介绍了kratos run命令和配置文件的读取方式,通过定义配置数据读取的结构,数据加载的方式来满足不同的需求。通过监听数据源的修改,如果有更改则进行同步修改。