From 1aa72efaf5e7b6c0bc3182c0f2582559e800ff29 Mon Sep 17 00:00:00 2001 From: wineandchord Date: Fri, 15 Sep 2023 10:17:24 +0800 Subject: [PATCH] config: add docs for business configuration (#20) --- .../business_configuration/trpc_cn.png | 3 + .../business_configuration/trpc_en.png | 3 + config/README.md | 276 +++++++++++------- config/README_CN.md | 150 ---------- config/README_zh_CN.md | 212 ++++++++++++++ 5 files changed, 395 insertions(+), 249 deletions(-) create mode 100644 .resources/user_guide/business_configuration/trpc_cn.png create mode 100644 .resources/user_guide/business_configuration/trpc_en.png delete mode 100644 config/README_CN.md create mode 100644 config/README_zh_CN.md diff --git a/.resources/user_guide/business_configuration/trpc_cn.png b/.resources/user_guide/business_configuration/trpc_cn.png new file mode 100644 index 0000000..103e0e0 --- /dev/null +++ b/.resources/user_guide/business_configuration/trpc_cn.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02d4f1d8f2eec5abfdded2d801c8bfc2352964ddcc4ece273baefd1aa1ff87c6 +size 25812 diff --git a/.resources/user_guide/business_configuration/trpc_en.png b/.resources/user_guide/business_configuration/trpc_en.png new file mode 100644 index 0000000..b6f3805 --- /dev/null +++ b/.resources/user_guide/business_configuration/trpc_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4fab14b1322d7aaaf963ff0d10cf3d984a0b40e47e2bad335ad53abd8226e483 +size 167137 diff --git a/config/README.md b/config/README.md index 645779e..707ae1e 100644 --- a/config/README.md +++ b/config/README.md @@ -1,149 +1,227 @@ -# tRPC-Go Config Package +English | [中文](./README_zh_CN.md) -trpc-go/config is a componentized config package, it provides an easy way to read multi config content sources and multi config file types. +# Introduction -## Features +Configuration management plays an extremely important role in the microservices governance system. The tRPC framework provides a set of standard interfaces for business program development, supporting the retrieval of configuration from multiple data sources, parsing configuration, and perceiving configuration changes. The framework shields the details of data source docking, simplifying development. This article aims to provide users with the following information: -- plug-in: could load configuration from multi content sources, such as local file, TConf, Rainbow, etc. +* What is business configuration and how does it differ from framework configuration. +* Some core concepts of business configuration such as: provider, codec, etc. +* How to use standard interfaces to retrieve business configurations. +* How to perceive changes in configuration items. -- hot loading: auto loading the new one when configuration changes. +# Concept -- default setting: if configuration is lost for some reasons, the default value can be used. +## What is Business Configuration? -## Core Data Structure +Business configuration refers to configuration used by the business, defined by the business program in terms of format, meaning, and parameter range. The tRPC framework does not use business configuration nor care about its meaning. The framework only focuses on how to retrieve configuration content, parse configuration, discover configuration changes, and notify the business program. -- Config: common configuration interface, provides the reading config operation. +The difference between business configuration and framework configuration lies in the subject using the configuration and the management method. Framework configuration is used for tRPC framework and defined by the framework in terms of format and meaning. Framework configuration only supports local file reading mode and is read during program startup to initialize the framework. Framework configuration does not support dynamic updates; if the framework configuration needs to be updated, the program needs to be restarted. -- ConfigLoader: config loader interface, by this we can implement config loading strategy. +On the other hand, business configuration supports retrieval from multiple data sources such as local files, configuration centers, databases, etc. If the data source supports configuration item event listening, tRPC framework provides a mechanism to achieve dynamic updating of configurations. -- Codec: config codec interface, by this we can support multi config type. +## Managing Business Configuration -- DataProvider: data provider interface, by this we can support multi content sources, such as `file`,`tconf`,`rainbow`. +For managing business configuration, we recommend the best practice of using a configuration center. Using a configuration center has the following advantages: -## How To Use +* Avoiding source code leaking sensitive information +* Dynamically updating configurations for services +* Allowing multiple services to share configurations and avoiding multiple copies of the same configuration +* Supporting gray releases, configuration rollbacks, and having complete permission management and operation logs +* Business configuration also supports local files. For local files, most use cases involve clients being used as independent tools or programs in the development and debugging phases. The advantage is that it can work without relying on an external system. -```go -import ( - "trpc.group/trpc-go/trpc-go/config" -) +## What is Multiple Data Sources? +A data source is the source from which configuration is retrieved and where it is stored. Common data sources include: file, etcd, configmap, etc. The tRPC framework supports setting different data sources for different business configurations. The framework uses a plugin-based approach to extend support for more data sources. In the implementation principle section later, we will describe in detail how the framework supports multiple data sources. -// load local config file: config.WithProvider("file") -config.Load("../testdata/trpc_go.yaml", config.WithCodec("yaml"), config.WithProvider("file")) +## What is Codec? -// the default data provider is local file. -config.Load("../testdata/trpc_go.yaml", config.WithCodec("yaml")) +In business configuration, Codec refers to the format of configurations retrieved from configuration sources. Common configuration file formats include: YAML, JSON, TOML, etc. The framework uses a plugin-based approach to extend support for more decoding formats. -// load TConf config file: config.WithProvider("tconf") -c, _ := config.Load("test.yaml", config.WithCodec("yaml"), config.WithProvider("tconf")) +# Implementation Principle +To better understand the use of configuration interfaces and how to dock with data sources, let's take a brief look at how the configuration interface module is implemented. The following diagram is a schematic diagram of the configuration module implementation (not a code implementation class diagram): -// read bool value -c.GetBool("server.debug", false) +![trpc](/.resources/user_guide/business_configuration/trpc_en.png) -// read string value -c.GetString("server.app", "default") +The config interface in the diagram provides a standard interface for business code to retrieve configuration items, and each data type has an independent interface that supports returning default values. +We have already introduced Codec and DataProvider in section 2, and these two modules provide standard interfaces and registration functions to support plugin-based encoding/decoding and data source. Taking multi-data sources as an example, DataProvider provides the following three standard interfaces: + +* Read(): provides how to read the original data of the configuration (raw bytes). +* Watch(): provides a callback function that the framework executes when the data source's data changes. + +```go +type DataProvider interface { + Name() string + Read(string) ([]byte, error) + Watch(ProviderCallback) +} ``` -### Concurrency-safely watching remote configuration changes +Finally, let's see how to retrieve a business configuration by specifying the data source and decoder: ```go -import ( - "sync/atomic" - ... -) +// Load etcd configuration file: config.WithProvider("etcd"). +c, _ := config.Load("test.yaml", config.WithCodec("yaml"), config.WithProvider("etcd")) +// Read String type configuration. +c.GetString("auth.user", "admin") +``` -type yamlFile struct { - Server struct { - App string - } -} +In this example, the data source is the etcd configuration center, and the business configuration file in the data source is "test.yaml". When the ConfigLoader obtains the "test.yaml" business configuration, it specifies to use YAML format to decode the data content. Finally, the c.GetString("server.app", "default") function is used to obtain the value of the auth.user configuration item in the test.yaml file. -// see: https://golang.org/pkg/sync/atomic/#Value -var cfg atomic.Value // Concurrency-safe Value +# Interface Usage -// watching tconf remote config changes by Watch interface in trpc-go/config -c, _ := config.Get("tconf").Watch(context.TODO(), "test.yaml") +This article only introduces the corresponding interfaces from the perspective of using business configurations. If users need to develop data source plugins or Codec plugins, please refer to tRPC-Go Development Configuration Plugin. For specific interface parameters, please refer to the tRPC-Go API manual. -go func() { - for r := range c { - yf := &yamlFile{} - fmt.Printf("event: %d, value: %s", r.Event(), r.Value()) +The tRPC-Go framework provides two sets of interfaces for "reading configuration items" and "watching configuration item changes". - if err := yaml.Unmarshal([]byte(r.Value()), yf); err == nil { - cfg.Store(yf) - } +## Reading Configuration Items - } -}() +Step 1: Selecting Plugins -// after init config, we can get newest config object by atomic.Value.Load() -cfg.Load().(*yamlFile) +Before using the configuration interface, it is necessary to configure data source plugins and their configurations in advance. Please refer to the Plugin Ecology for plugin usage. The tRPC framework supports local file data sources by default. + +Step 2: Plugin Initialization +Since the data source is implemented using a plugin, tRPC framework needs to initialize all plugins in the server initialization function by reading the "trpc_go.yaml" file. The read operation of business configuration must be carried out after completing trpc.NewServer(). + +```go +import ( + trpc "trpc.group/trpc-go/trpc-go" +) +// Plugin system will be initialized when the server is instantiated, and all configuration read operations need to be performed after this. +trpc.NewServer() ``` -### How To Mock Watching -When writing unit test code, we need create a stub for related function, and replace the original implementation. Other function is in the same way, here is the example of Watch(). +Step 3: Loading Configuration +Load configuration file from data source and return config data structure. The data source type and Codec format can be specified, with the framework defaulting to "file" data source and "YAML" Codec. The interface is defined as follows: ```go +// Load configuration file: path is the path of the configuration file. +func Load(path string, opts ...LoadOption) (Config, error) +// Change Codec type, default is "YAML" format. +func WithCodec(name string) LoadOption +// Change data source, default is "file". +func WithProvider(name string) LoadOption +``` -import ( - "context" - "testing" +The sample code is as follows: - "trpc.group/trpc-go/trpc-go/config" - mock "trpc.group/trpc-go/trpc-go/config/mockconfig" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" -) +```go +// Load etcd configuration file: config.WithProvider("etcd"). +c, _ := config.Load("test1.yaml", config.WithCodec("yaml"), config.WithProvider("etcd")) +// Load local configuration file, codec is json, data source is file. +c, _ := config.Load("../testdata/auth.yaml", config.WithCodec("json"), config.WithProvider("file")) +// Load local configuration file, default Codec is yaml, data source is file. +c, _ := config.Load("../testdata/auth.yaml") +``` -func TestWatch(t *testing.T) { - ctrl := gomock.NewController(t) - - // mock rainbow and its key. - mockKey := "test.json" - mockProvider := "rainbow" - - // watching data comes from writing mock channel. - mockChan := make(chan config.Response, 1) - mockResp := &mockResponse{val: "mock-value"} - mockChan <- mockResp - - // mock the process of Watch. - kv := mock.NewMockKVConfig(ctrl) - kv.EXPECT().Name().Return(mockProvider).AnyTimes() - m := kv.EXPECT().Watch(gomock.Any(), mockKey, gomock.Any()).AnyTimes() - m.DoAndReturn(func(ctx context.Context, key string, opts ...config.Option) (<-chan config.Response, error) { - return mockChan, nil - }) - // register kv config. - config.Register(kv) - - // action - got, err := config.Get(mockProvider).Watch(context.TODO(), mockKey) - assert.Nil(t, err) - assert.NotNil(t, got) - - resp := <-got - assert.Equal(t, mockResp, resp) -} +Step 4: Retrieving Configuration Items +Get the value of a specific configuration item from the config data structure. Default values can be set, and the framework provides the following standard interfaces: -type mockResponse struct { - val string +```go +// Config general interface. +type Config interface { + Load() error + Reload() + Get(string, interface{}) interface{} + Unmarshal(interface{}) error + IsSet(string) bool + GetInt(string, int) int + GetInt32(string, int32) int32 + GetInt64(string, int64) int64 + GetUint(string, uint) uint + GetUint32(string, uint32) uint32 + GetUint64(string, uint64) uint64 + GetFloat32(string, float32) float32 + GetFloat64(string, float64) float64 + GetString(string, string) string + GetBool(string, bool) bool + Bytes() []byte } +``` + +The sample code is as follows: -func (r *mockResponse) Value() string { - return r.val +```go +// Read bool type configuration. +c.GetBool("server.debug", false) +// Read String type configuration. +c.GetString("server.app", "default") +``` + +## Watching configuration item changes + +The framework provides a Watch mechanism for business programs to define and execute their own logic based on received configuration item change events in KV-type configuration centers. The monitoring interface is designed as follows: + +```go +// Get retrieves kvconfig by name. +func Get(name string) KVConfig + +// KVConfig is the interface for KV configurations. +type KVConfig interface { + KV + Watcher + Name() string } -func (r *mockResponse) MetaData() map[string]string { - return nil +// Watcher is the interface for monitoring. +type Watcher interface { + // Watch monitors the changes of the configuration item key. + Watch(ctx context.Context, key string, opts ...Option) (<-chan Response, error) } -func (r *mockResponse) Event() config.EventType { - return config.EventTypeNull +// Response represents the response from the configuration center. +type Response interface { + // Value gets the value corresponding to the configuration item. + Value() string + // MetaData provides additional metadata information. + // Configuration Option options can be used to carry extra functionality implementation of different configuration centers, such as namespace, group, lease, etc. + MetaData() map[string]string + // Event gets the type of the Watch event. + Event() EventType } +// EventType represents the types of events monitored for configuration changes. +type EventType uint8 +const ( + // EventTypeNull represents an empty event. + EventTypeNull EventType = 0 + // EventTypePut represents a set or update configuration event. + EventTypePut EventType = 1 + // EventTypeDel represents a delete configuration item event. + EventTypeDel EventType = 2 +) +``` +The following example demonstrates how a business program monitors the "test.yaml" file on etcd, +prints configuration item change events, and updates the configuration: + +```go +import ( + "sync/atomic" + // ... +) +type yamlFile struct { + Server struct { + App string + } +} +var cfg atomic.Value // Concurrent-safe Value. +// Listen to remote configuration changes on etcd using the Watch interface in trpc-go/config. +c, _ := config.Get("etcd").Watch(context.TODO(), "test.yaml") +go func() { + for r := range c { + yf := &yamlFile{} + fmt.Printf("Event: %d, Value: %s", r.Event(), r.Value()) + if err := yaml.Unmarshal([]byte(r.Value()), yf); err == nil { + cfg.Store(yf) + } + } +}() +// After the configuration is initialized, the latest configuration object can be obtained through the Load method of atomic.Value. +cfg.Load().(*yamlFile) ``` +# Data Source Integration + +Refer to [trpc-ecosystem/go-config-etcd](https://github.com/trpc-ecosystem/go-config-etcd). diff --git a/config/README_CN.md b/config/README_CN.md deleted file mode 100644 index b7d5aae..0000000 --- a/config/README_CN.md +++ /dev/null @@ -1,150 +0,0 @@ -# tRPC-Go 配置库 - -trpc-go/config是一个组件化的配置库,提供了一种简单方式读取多种内容源、多种文件类型的配置。 - -## 功能 - -- 插件化:根据配置需要可从多个内容源(本地文件、TConf 等)加载配置。 - -- 热加载:变更时自动载入新配置 - -- 默认设置:配置由于某些原因丢失时,可以回退使用默认值。 - -## 相关结构 - -- Config:配置的通用接口,提供了相关的配置读取操作。 - -- ConfigLoader:配置加载器,通过实现 ConfigLoader 相关接口以支持加载策略。 - -- Codec:配置编解码接口,通过实现 Codec 相关接口以支持多种类型配置。 - -- DataProvider: 内容源接口,通过实现 DataProvider 相关接口以支持多种内容源。目前支持`file`,`tconf`,`rainbow`等配置内容源。 - -## 如何使用 - -```go -import ( - "trpc.group/trpc-go/trpc-go/config" -) - - -// 加载本地配置文件:config.WithProvider("file") -config.Load("../testdata/trpc_go.yaml", config.WithCodec("yaml"), config.WithProvider("file")) - -// 默认的 DataProvider 是使用本地文件 -config.Load("../testdata/trpc_go.yaml", config.WithCodec("yaml")) - -// 加载 TConf 配置文件:config.WithProvider("tconf") -c, _ := config.Load("test.yaml", config.WithCodec("yaml"), config.WithProvider("tconf")) - -// 读取 bool 类型配置 -c.GetBool("server.debug", false) - -// 读取 String 类型配置 -c.GetString("server.app", "default") - -``` - -### 并发安全的监听远程配置变化 - -```go -import ( - "sync/atomic" - ... -) - -type yamlFile struct { - Server struct { - App string - } -} - -// 参考:https://golang.org/pkg/sync/atomic/#Value -var cfg atomic.Value // 并发安全的 Value - -// 使用trpc-go/config中Watch接口监听tconf远程配置变化 -c, _ := config.Get("tconf").Watch(context.TODO(), "test.yaml") - -go func() { - for r := range c { - yf := &yamlFile{} - fmt.Printf("event: %d, value: %s", r.Event(), r.Value()) - - if err := yaml.Unmarshal([]byte(r.Value()), yf); err == nil { - cfg.Store(yf) - } - - } -}() - -// 当配置初始化完成后,可以通过 atomic.Value 的 Load 方法获得最新的配置对象 -cfg.Load().(*yamlFile) - -``` - -### 如何 mock Watch -业务代码在单元测试时,需要对相关函数进行打桩 - -其他的方法需要 mock,也是使用相同的方法,先打桩替换你需要的实现 -```go - -import ( - "context" - "testing" - - "trpc.group/trpc-go/trpc-go/config" - mock "trpc.group/trpc-go/trpc-go/config/mockconfig" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" -) - -func TestWatch(t *testing.T) { - ctrl := gomock.NewController(t) - - // 模拟七彩石及对应的 Key - mockKey := "test.json" - mockProvider := "rainbow" - - // Watch 得到的所有数据都是通过写入 mockChan 去模拟 - mockChan := make(chan config.Response, 1) - mockResp := &mockResponse{val: "mock-value"} - mockChan <- mockResp - - // 模拟 Watch 的流程 - kv := mock.NewMockKVConfig(ctrl) - kv.EXPECT().Name().Return(mockProvider).AnyTimes() - m := kv.EXPECT().Watch(gomock.Any(), mockKey, gomock.Any()).AnyTimes() - m.DoAndReturn(func(ctx context.Context, key string, opts ...config.Option) (<-chan config.Response, error) { - return mockChan, nil - }) - // 注册 KVConfig - config.Register(kv) - - // action - got, err := config.Get(mockProvider).Watch(context.TODO(), mockKey) - assert.Nil(t, err) - assert.NotNil(t, got) - - resp := <-got - assert.Equal(t, mockResp, resp) -} - -type mockResponse struct { - val string -} - -func (r *mockResponse) Value() string { - return r.val -} - -func (r *mockResponse) MetaData() map[string]string { - return nil -} - -func (r *mockResponse) Event() config.EventType { - return config.EventTypeNull -} - - -``` - diff --git a/config/README_zh_CN.md b/config/README_zh_CN.md new file mode 100644 index 0000000..bbb6032 --- /dev/null +++ b/config/README_zh_CN.md @@ -0,0 +1,212 @@ +[English](./README.md) | 中文 + +# 前言 + +配置管理是微服务治理体系中非常重要的一环,tRPC 框架为业务程序开发提供了一套支持从多种数据源获取配置,解析配置和感知配置变化的标准接口,框架屏蔽了和数据源对接细节,简化了开发。通过本文的介绍, 旨在为用户提供以下信息: +- 什么是业务配置,它和框架配置的区别 +- 业务配置的一些核心概念(比如: provider,codec...) +- 如何使用标准接口获取业务配置 +- 如何感知配置项的变化 + +# 概念介绍 +## 什么是业务配置 +业务配置是供业务使用的配置,它由业务程序定义配置的格式,含义和参数范围,tRPC 框架并不使用业务配置,也不关心配置的含义。框架仅仅关心如何获取配置内容,解析配置,发现配置变化并告知业务程序。 + +业务配置和框架配置的区别在于使用配置的主体和管理方式不一样。框架配置是供 tRPC 框架使用的,由框架定义配置的格式和含义。框架配置仅支持从本地文件读取方式,在程序启动是读取配置,用于初始化框架。框架配置不支持动态更新配置,如果需要更新框架配置,则需要重启程序。 + +而业务配置则不同,业务配置支持从多种数据源获取配置,比如:本地文件,配置中心,数据库等。如果数据源支持配置项事件监听功能,tRPC 框架则提供了机制以实现配置的动态更新。 + +## 如何管理业务配置 +对于业务配置的管理,我们建议最佳实践是使用配置中心来管理业务配置,使用配置中心有以下优点: +- 避免源代码泄露敏感信息 +- 服务动态更新配置 +- 多服务共享配置,避免一份配置拥有多个副本 +- 支持灰度发布,配置回滚,拥有完善的权限管理和操作日志 + +业务配置也支持本地文件。对于本地文件,大部分使用场景是客户端作为独立的工具使用,或者程序在开发调试阶段使用。好处在于不需要依赖外部系统就能工作。 + +## 什么是多数据源 +数据源就获取配置的来源,配置存储的地方。常见的数据源包括:file,etcd,configmap 等。tRPC 框架支持对不同业务配置设定不同的数据源。框架采用插件化方式来扩展对更多数据源的支持。在后面的实现原理章节,我们会详细介绍框架是如何实现对多数据源的支持的。 + +## 什么是 Codec +业务配置中的 Codec 是指从配置源获取到的配置的格式,常见的配置文件格式为:yaml,json,toml 等。框架采用插件化方式来扩展对更多解码格式的支持。 + +# 实现原理 +为了更好的了解配置接口的使用,以及如何和数据源做对接,我们简单看看配置接口模块是如何实现的。下面这张图是配置模块实现的示意图(非代码实现类图): + +![trpc](/.resources/user_guide/business_configuration/trpc_cn.png) + +图中的 config 接口为业务代码提供了获取配置项的标准接口,每种数据类型都有一个独立的接口,接口支持返回 default 值。 + +Codec 和 DataProvider 这两个模块都提供了标准接口和注册函数以支持编解码和数据源的插件化。以实现多数据源为例,DataProvider 提供了以下三个标准接口,其中 Read 函数提供了如何读取配置的原始数据(未解码),而 Watch 函数提供了 callback 函数,当数据源的数据发生变化时,框架会执行此 callback 函数。 +```go +type DataProvider interface { + Name() string + Read(string) ([]byte, error) + Watch(ProviderCallback) +} +``` + +最后我们来看看,如何通过指定数据源,解码器来获取一个业务配置项: +```go +// 加载 etcd 配置文件:config.WithProvider("etcd") +c, _ := config.Load("test.yaml", config.WithCodec("yaml"), config.WithProvider("etcd")) +// 读取 String 类型配置 +c.GetString("auth.user", "admin") +``` +在这个示例中,数据源为 etcd 配置中心,数据源中的业务配置文件为“test.yaml”。当 ConfigLoader 获取到"test.yaml"业务配置时,指定使用 yaml 格式对数据内容进行解码。最后通过`c.GetString("server.app", "default")`函数来获取 test.yaml 文件中`auth.user`这个配置型的值。 + +# 接口使用 +本文仅从使用业务配置的角度来介绍相应的接口,如何用户需要开发数据源插件或者 Codec 插件,请参考 [tRPC-Go 开发配置插件](todo)。 + +tRPC-Go 框架提供了两套接口分别用于 “读取配置项” 和 “监听配置项” +## 获取配置项 +**第一步:选择插件** +在使用配置接口之前需要提前配置好数据源插件,以及插件配置。tRPC 框架默认支持本地文件数据源。 + +**第二步:插件初始化** +由于数据源采用的是插件方式实现的,需要 tRPC 框架在服务端初始化函数中,通过读取“trpc_go.yaml”文件来初始化所有插件。业务配置的读取操作必须在完成`trpc.NewServer()`之后 +```go +import ( + trpc "trpc.group/trpc-go/trpc-go" +) + +// 实例化 server 时会初始化插件系统,所有配置读取操作需要在此之后 +trpc.NewServer() +``` + +**第三步:加载配置** +从数据源加载配置文件,返回 config 数据结构。可指定数据源类型和编解码格式,框架默认为“file”数据源和“yaml”编解码。接口定义为: +```go +// 加载配置文件:path 为配置文件路径 +func Load(path string, opts ...LoadOption) (Config, error) +// 更改编解码类型,默认为“yaml”格式 +func WithCodec(name string) LoadOption +// 更改数据源,默认为“file” +func WithProvider(name string) LoadOption +``` + +示例代码为: +```go +// 加载 etcd 配置文件:config.WithProvider("etcd") +c, _ := config.Load("test1.yaml", config.WithCodec("yaml"), config.WithProvider("etcd")) + +// 加载本地配置文件,codec 为 json,数据源为 file +c, _ := config.Load("../testdata/auth.yaml", config.WithCodec("json"), config.WithProvider("file")) + +// 加载本地配置文件,默认为 codec 为 yaml,数据源为 file +c, _ := config.Load("../testdata/auth.yaml") +``` + +**第四步:获取配置项** +从 config 数据结构中获取指定配置项值。支持设置默认值,框架提供以下标准接口: +```go +// Config 配置通用接口 +type Config interface { + Load() error + Reload() + Get(string, interface{}) interface{} + Unmarshal(interface{}) error + IsSet(string) bool + GetInt(string, int) int + GetInt32(string, int32) int32 + GetInt64(string, int64) int64 + GetUint(string, uint) uint + GetUint32(string, uint32) uint32 + GetUint64(string, uint64) uint64 + GetFloat32(string, float32) float32 + GetFloat64(string, float64) float64 + GetString(string, string) string + GetBool(string, bool) bool + Bytes() []byte +} +``` + +示例代码为: +```go +// 读取 bool 类型配置 +c.GetBool("server.debug", false) + +// 读取 String 类型配置 +c.GetString("server.app", "default") +``` + +## 监听配置项 +对于 KV 型配置中心,框架提供了 Watch 机制供业务程序根据接收的配置项变更事件,自行定义和执行业务逻辑。监控接口设计如下: +```go + +// Get 根据名字使用 kvconfig +func Get(name string) KVConfig + +// KVConfig kv 配置 +type KVConfig interface { + KV + Watcher + Name() string +} + +// 监控接口定义 +type Watcher interface { + // Watch 监听配置项 key 的变更事件 + Watch(ctx context.Context, key string, opts ...Option) (<-chan Response, error) +} + +// Response 配置中心响应 +type Response interface { + // Value 获取配置项对应的值 + Value() string + // MetaData 额外元数据信息 + // 配置 Option 选项,可用于承载不同配置中心的额外功能实现,例如 namespace,group, 租约等概念 + MetaData() map[string]string + // Event 获取 Watch 事件类型 + Event() EventType +} + +// EventType 监听配置变更的事件类型 +type EventType uint8 +const ( + // EventTypeNull 空事件 + EventTypeNull EventType = 0 + // EventTypePut 设置或更新配置事件 + EventTypePut EventType = 1 + // EventTypeDel 删除配置项事件 + EventTypeDel EventType = 2 +) +``` + +下面示例展示了业务程序监控 etcd 上的“test.yaml”文件,打印配置项变更事件并更新配置。 +```go +import ( + "sync/atomic" + // ... +) + +type yamlFile struct { + Server struct { + App string + } +} + +var cfg atomic.Value // 并发安全的 Value + +// 使用 trpc-go/config 中 Watch 接口监听 etcd 远程配置变化 +c, _ := config.Get("etcd").Watch(context.TODO(), "test.yaml") + +go func() { + for r := range c { + yf := &yamlFile{} + fmt.Printf("event: %d, value: %s", r.Event(), r.Value()) + + if err := yaml.Unmarshal([]byte(r.Value()), yf); err == nil { + cfg.Store(yf) + } + } +}() + +// 当配置初始化完成后,可以通过 atomic.Value 的 Load 方法获得最新的配置对象 +cfg.Load().(*yamlFile) +``` + +# 数据源实现 + +参考:[trpc-ecosystem/go-config-etcd](https://github.com/trpc-ecosystem/go-config-etcd)