client 接入指南

配置被 Sail 管理起来后,如何获取这些配置呢?目前市面的配置中心无非两种流派:

  1. SDK 集成

类似 Nacos、Apollo 都是这种方式,提供一个语言特定的 sdk,集成 sdk 即可。

  1. sidecar 外挂

类似 confd 是这种方式,外挂一个进程,专门负责输出和更新配置文件,程序还是读取配置,侵入性小。

这两种方式,sail 的 client 都是支持的,请看下图:

原理

由于 sail-client 会把从 etcd 中读取的配置备份到文件,并保持文件内容的更新,所以,从使用者的角度来说, 既可以把 sail-client 集成到代码中,也可以单独运行 sail-client,然后去读取备份的配置文件。

安装

代码接入:

go get github.com/HYY-yu/sail-client

单独运行:

git clone https://github.com/HYY-yu/sail-client.git
# 修改 cfg.toml 为你的配置
vim cfg.toml
go build -o service ./cmd/main.go
./service

# 打包成 docker 镜像
CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -o service ./cmd/main.go
docker build --build-arg=serviceName=sail-client -f ./deploy/Dockerfile .

代码接入使用方式

1. New 一个 sail 实例

前面我们说了,sail-client 运行需要元配置,而元配置信息可以从 Sail 中获取。 获取后,可以通过 toml环境变量启动 flag 注入:

// 通过读取 toml 文件
s := sail.NewWithToml("your_config.toml")
if s.Err() != nil{
  panic(s.Err())
}
// 通过读取环境变量
s := sail.NewWithEnv()
if s.Err() != nil{
  panic(s.Err())
}
// 通过读取启动 flag
s := sail.NewWithFlag()
if s.Err() != nil{
  panic(s.Err())
}

2. 运行 Pull() 获取配置

err := s.Pull()
if err != nil{
  panic(err)
}

第二步就要去 ETCD 拉配置了,也很容易理解。

这里如果访问不到 ETCD,会尝试去读取备份配置文件,并持续重连 etcd,不会报错,只会打印警告日志,需要注意。

3. 从代码中获取配置

本质上,sail-client 内部就是把配置信息存到了 viper 实例,所以你如果用的是 viper,那获取方式跟 viper 是一模一样的。

如果你没用过 viper,那可以看看以下简单的介绍:

# 假设有如下两个配置文件:
# mysql.toml
[mysql]
addr="127.0.0.1:3306"
database="sail"

# redis.yaml
redis:
  host: 127.0.0.1
  port: 6789
  db: 1

通过 Key 就可以获取到配置信息,嵌套的 key 可以用.分隔。

// GetXXX ,MustGetXXX 获取配置

redisHost := s.MustGetString("redis.host")
redisDB := s.MustGetInt("redis.db")
mysqlDb := s.MustGetString("mysql.database")
...

如果你的代码是使用了一个结构体,比如:

type Config struct{
   Mysql struct{
      Addr string
      Database string
   }
   Redis struct{
      Host string 
      Port int
      Db int 
   }
}

可以通过 viper 的 Unmarshal 填充,注意结构体不会被自动更新:

 cfg := &Config{}
 // 直接获取一个 viper 实例,包含了所有的配置
 newViper,err:= s.MergeVipers()
 if err != nil{
    panic(err)
 }
 newViper.Unmarshal(&cfg)

在配置更新时,收到回调,比如,在收到配置更新时,更新上面的结构体Config

// 需要在初始化时,传回调函数
sail := sailclient.NewWithToml("./cfg.toml",
          sailclient.WithOnConfigChange(func(configFileKey string, s *sailclient.Sail) {
            log.Println("find key change - ",configFileKey)
            log.Println("new value: ",s.MustGetString("test.log"))

            // 更新结构体
            newViper,err:= s.MergeVipers()
            if err != nil{
              panic(err)
            }
            newViper.Unmarshal(&cfg)
          })
        )
if sail.Err() != nil {
  log.Fatalln(sail.Err())
}

你也可以在回调函数中做更多的操作。

4. 更多使用技巧,请阅读 sail-client 源码

阅读源码并不难,sail-client 实现是很简单的,效率会比看文档高多了。

关于配置文件中重复 key 的提示

重复 key 的支持,这里需要着重提下,如果你能保证你的配置文件内不会有重复的 key,那可以不看下面的内容。

注意,这不是重复 key:

# 假设有如下两个配置文件:
# mysql.toml
[mysql]
addr="127.0.0.1:3306"
database="sail"

# redis.yaml
redis:
  addr: 127.0.0.1
  port: 6789
  db: 1

# 虽然两个配置文件中都有 addr,但它们属于不同的父 key。

这是重复 key:

# 假设有如下两个配置文件:
# mysql.toml
addr="127.0.0.1:3306"
database="sail"

# redis.yaml
addr: 127.0.0.1
port: 6789

碰到重复key,需要用 GetXXWithName 限定:

mysqlAddr := s.GetStringWithName("addr","mysql.toml")
redisAddr := s.GetStringWithName("addr","redis.yaml")

重复 key 要 merge 成一个 viper 实例,需要用下面的方法:

newViper,err := s.MergeVipersWithName()
if err != nil{
  panic(err)
}

// 获取时,带上文件名
newViper.Get("mysql.toml.addr")