kratos配置

简介: kratos配置

kratos配置

简介

  微服务或者说云原生应用的配置最佳实践是将配置文件和应用代码分开管理——不将配置文件放入代码仓库,也不打包进容器镜像,而是在服务运行时,把配置文件挂载进去或者直接从配置中心加载。Kratos的config组件就是用来帮助应用从各种配置源加载配置。

设计理念

支持多种配置源

   Kratos定义了标准化的Source和Watcher接口来适配各种配置源。

package config

// 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
}

  框架内置了本地文件file环境变量env的实现。

  另外,在contrib/config下面,提供了如下的配置中心的适配供使用:

  如果上述的配置加载方式无法涵盖您的环境,您也可以通过实现接口来适配您自己的配置加载方式。

支持多种配置格式

  配置组件复用了encoding​中的反序列化逻辑作为配置解析使用。
框架将根据配置文件类型匹配对应的Codec,进行配置文件的解析。
您也可以通过实现Codec并用encoding.RegisterCodec​方法,将它注册进去,来解析其它格式的配置文件。
配置文件类型的提取,根据配置源具体实现不同而略有区别,内置的file是把文件后缀作为文件类型的,其它配置源插件的具体逻辑请参考对应的文档。
默认支持以下格式的解析:

  • json
  • proto
  • xml
  • yaml

热更新

  Kratos的config组件支持配置的热更新,可以使用配置中心配合config的热更新功能,在服务不重新发布/不停机/不重启的情况下,在线更新服务的配置,修改服务的一些行为。

配置合并

  在config组件中,所有的配置源中的配置(文件)将被逐个读出,分别解析成map,并合并到一个map中去。因此在加载完毕后,不需要再理会配置的文件名,不用文件名来进行查找,而是用内容中的结构来对配置的值进行索引即可。设计和编写配置文件时,请注意各个配置文件中,根层级的key不要重复,否则可能会被覆盖
配置文件的各层级将分别合并,在key冲突时会发生覆盖,而具体的覆盖顺序,会由配置源实现中的读取顺序决定,因此这里重新提醒一下,各个配置文件中,根层级的key不要重复,也不要依赖这个覆盖的特性,从根本上避免不同配置文件的内容互相覆盖造成问题

API概览

New

  New方法使用选项创建一个Config

func New(opts ...Option) Config

Option

  可以通过Option注入自定义的组件, WithSource这个是必定会用到,其他一般不怎么会用到。

  • WithDecoder: 自定义解码器
  • WithLogger: 注入一个日志组件,该接口已经被废弃,可以使用全局日志组件替代
  • WithResolver: 自定义占位符处理器
  • WithSource: 添加配置源
func WithDecoder(d Decoder) Option
func WithLogger(l log.Logger) Option
func WithResolver(r Resolver) Option
func WithSource(s ...Source) Option

Config

type Config interface {
   
    Load() error 
    Scan(v interface{
   }) error
    Value(key string) Value
    Watch(key string, o Observer) error
    Close() error
}
  • Load: 加载配置源
  • Scan: 将配置反射到结构体中
  • Value: 获取配置的值
  • Watch: 监听配置更新
  • Close: 关闭所有watch

File

  完整代码

初始化配置源

    path := "config.yaml"
    // 初始化配置源
    c := config.New(
        config.WithSource(
            file.NewSource(path),
        ),
    )

读取配置

  读取配置可以使用Scan来反射到结构体也可以使用Value获取指定键值

使用Scan

    var v struct {
   
        Service struct {
   
            Name    string `json:"name"`
            Version string `json:"version"`
        } `json:"service"`
    }

    // Unmarshal the config to struct
    if err := c.Scan(&v); err != nil {
   
        panic(err)
    }

  如果通过Scan方法来读取, 修改文件内容后结构体的也会跟着更改

    for {
   
        version, err := c.Value("service.version").String()
        if err != nil {
   
            panic(err)
        }
        log.Printf("version: %s", version)
        time.Sleep(time.Second)
    }

// 2023/05/07 11:51:47 version: v1.0.0
// 2023/05/07 11:51:48 version: v1.0.011

使用Value

    name, err := c.Value("service.name").String()
    if err != nil {
   
        panic(err)
    }
    log.Printf("service: %s", name)

监听配置变更

  通过.Watch​方法,可以监听配置中某个字段的变更,在本地或远端的配置中心有配置文件变更时,执行回调函数进行自定义的处理

    // watch key
    if err := c.Watch("service.name", func(key string, value config.Value) {
   
        log.Printf("config changed: %s = %v\n", key, value)
    }); err != nil {
   
        panic(err)
    }

env

  完整代码

  如果有配置需要从环境变量读取可以配置env.NewSource

c := config.New(
    config.WithSource(
        // 添加前缀为 KRATOS_ 的环境变量,不需要的话也可以设为空字符串
        env.NewSource("KRATOS_"),
        // 添加配置文件
        file.NewSource(path),
    ))

// 加载配置源:
if err := c.Load(); err != nil {
   
    log.Fatal(err)
}

// 获取环境变量 KRATOS_PORT 的值,这里用去掉前缀的名称进行读取
port, err := c.Value("PORT").String()

  除了上面使用Value方法直接读的方式,也可以在配置文件内容里使用占位符来把环境变量中的值渲染进去:

service:
  name: "kratos_app"
http:
  server:
    # 使用 service.name 的值
    name: "${service.name}"
    # 使用环境变量 PORT 替换,若不存在,使用默认值 8080
    port: "${PORT:8080}"
    # 使用环境变量 TIMEOUT 替换,无默认值
    timeout: "$TIMEOUT"

consul

部署consul

  这里我们使用docker-compose部署

version: "3.0"

services:
  consul:
    container_name: consul
    image: consul
    restart: always
    ports:
      - 8500:8500
    command: ["consul","agent","-server","-bootstrap","-data-dir","/consul","-ui","-bind","127.0.0.1","-client","0.0.0.0"]

  添加配置

docker exec -it consul consul kv put http/server/port 8000

  代码实现

package main

import (
    "github.com/go-kratos/kratos/contrib/config/consul/v2"
    "github.com/go-kratos/kratos/v2/config"
    "github.com/hashicorp/consul/api"
    "log"
)

func main() {
   
    consulClient, err := api.NewClient(&api.Config{
   
        Address: "127.0.0.1:8500",
    })
    if err != nil {
   
        panic(err)
    }
    cs, err := consul.New(consulClient, consul.WithPath("http/server"))
    // consul中需要标注文件后缀,kratos读取配置需要适配文件后缀
    // The file suffix needs to be marked, and kratos needs to adapt the file suffix to read the configuration.
    if err != nil {
   
        panic(err)
    }
    c := config.New(config.WithSource(cs))
    defer c.Close()

    // load sources before get
    if err := c.Load(); err != nil {
   
        log.Fatalln(err)
    }

    // acquire config value
    foo, err := c.Value("port").String()
    if err != nil {
   
        log.Fatalln(err)
    }

    log.Println(foo)
}

etcd

部署etcd

  这里我们使用docker-compose部署

version: "3.0"

networks:
  etcd-net:           # 网络
    driver: bridge    # 桥接模式

volumes:
  etcd1_data:         # 挂载到本地的数据卷名
    driver: local
  etcd2_data:
    driver: local
  etcd3_data:
    driver: local
###
### etcd 其他环境配置见:https://doczhcn.gitbook.io/etcd/index/index-1/configuration
###
services:
  etcd1:
    image: bitnami/etcd:latest  # 镜像
    container_name: etcd1       # 容器名 --name
    restart: always             # 总是重启
    networks:
      - etcd-net                # 使用的网络 --network
    ports:                      # 端口映射 -p
      - "20000:2379"
      - "20001:2380"
    environment:                # 环境变量 --env
      - ALLOW_NONE_AUTHENTICATION=yes                       # 允许不用密码登录
      - ETCD_NAME=etcd1                                     # etcd 的名字
      - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd1:2380  # 列出这个成员的伙伴 URL 以便通告给集群的其他成员
      - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380           # 用于监听伙伴通讯的URL列表
      - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379         # 用于监听客户端通讯的URL列表
      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd1:2379        # 列出这个成员的客户端URL,通告给集群中的其他成员
      - ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster             # 在启动期间用于 etcd 集群的初始化集群记号
      - ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380 # 为启动初始化集群配置
      - ETCD_INITIAL_CLUSTER_STATE=new                      # 初始化集群状态
    volumes:
      - etcd1_data:/bitnami/etcd                            # 挂载的数据卷

  etcd2:
    image: bitnami/etcd:latest
    container_name: etcd2
    restart: always
    networks:
      - etcd-net
    ports:
      - "20002:2379"
      - "20003:2380"
    environment:
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_NAME=etcd2
      - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd2:2380
      - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
      - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd2:2379
      - ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster
      - ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
      - ETCD_INITIAL_CLUSTER_STATE=new
    volumes:
      - etcd2_data:/bitnami/etcd

  etcd3:
    image: bitnami/etcd:latest
    container_name: etcd3
    restart: always
    networks:
      - etcd-net
    ports:
      - "20004:2379"
      - "20005:2380"
    environment:
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_NAME=etcd3
      - ETCD_INITIAL_ADVERTISE_PEER_URLS=http://etcd3:2380
      - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
      - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd3:2379
      - ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster
      - ETCD_INITIAL_CLUSTER=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380
      - ETCD_INITIAL_CLUSTER_STATE=new
    volumes:
      - etcd3_data:/bitnami/etcd

  然后添加一个配置

docker exec -it etcd1 etcdctl put /app-config hello

  代码实现读取配置

package main

import (
    "log"
    "time"

    clientv3 "go.etcd.io/etcd/client/v3"
    "google.golang.org/grpc"

    cfg "github.com/go-kratos/kratos/contrib/config/etcd/v2"
    "github.com/go-kratos/kratos/v2/config"
)

func main() {
   
    // create an etcd client
    client, err := clientv3.New(clientv3.Config{
   
        Endpoints:   []string{
   "127.0.0.1:20000", "127.0.0.1:20002", "127.0.0.1:20004"},
        DialTimeout: time.Second,
        DialOptions: []grpc.DialOption{
   grpc.WithBlock()},
    })
    if err != nil {
   
        log.Fatal(err)
    }

    // configure the source, "path" is required
    source, err := cfg.New(client, cfg.WithPath("/app-config"), cfg.WithPrefix(true))
    if err != nil {
   
        log.Fatalln(err)
    }

    // create a config instance with source
    c := config.New(config.WithSource(source))
    defer c.Close()

    // load sources before get
    if err := c.Load(); err != nil {
   
        log.Fatalln(err)
    }

    // acquire config value
    foo, err := c.Value("/app-config").String()
    if err != nil {
   
        log.Fatalln(err)
    }

    log.Println(foo)

}

参考资料

  kratos doc 配置

  kratos config consul

  kratos config etcd

  docker hub etcd

  etcd中文文档配置

  docker-compose 搭建 etcd 集群

  docker-compose部署consul及使用

相关文章
|
SQL 存储 Cloud Native
seata-golang 接入指南
seata-golang 是一个分布式事务框架,实现了 AT 模式和 TCC 模式,AT 模式相较 TCC 模式对代码的入侵性更小、需要开发的接口更少;但 AT 模式对事务操作的数据持有全局锁,从这点来说,TCC 模式性能更好。
seata-golang 接入指南
|
7天前
|
人工智能 JSON 监控
探索Viper-适用于GoLang的完整配置解决方案
探索Viper-适用于GoLang的完整配置解决方案
|
5月前
|
编译器 Shell Go
|
6月前
|
Go API 微服务
Golang微服务框架Kratos轻松集成并使用Swagger UI
在我们的开发当中,调试接口,测试接口,提供接口文档给前端,那都是非常频繁的工作内容。 那么,我们需要用什么方法和工具来实施这些工作内容呢? Swagger,或者说OpenAPI。
120 0
|
6月前
|
开发框架 JavaScript .NET
Golang微服务框架kratos实现SignalR服务
SignalR 在可用的情况下使用新的 WebSocket 传输,并在必要时回退到旧传输。 虽然当然可以直接使用 WebSocket 编写应用,但使用 SignalR 意味着需要实现的许多额外功能已经为你完成。 最重要的是,这意味着你可以编写应用代码以利用 WebSocket,而无需担心为旧客户端创建单独的代码路径。 SignalR 还可以避免担心 WebSocket 的更新,因为 SignalR 已更新以支持基础传输中的更改,从而为应用程序提供跨 WebSocket 版本的一致接口。
37 0
|
12月前
|
Dubbo Java 应用服务中间件
dubbo-cli 和 grpc-cli
dubbo-cli 和 grpc-cli
105 0
|
12月前
|
关系型数据库 MySQL Go
(7)go-micro微服务zap日志配置
(7)go-micro微服务zap日志配置
155 0
|
Java Go API
Grpc初体验
Grpc初体验
103 0
|
资源调度 NoSQL 关系型数据库
Nestjs gRPC 官方demo详解
Nestjs gRPC 官方demo详解
971 1
Nestjs gRPC 官方demo详解
|
Dubbo Java 应用服务中间件
使用dubbo-go搭建dubbo接口测试平台
作为接口测试平台,没办法引入所有提供方定义的接口jar包,可以有以下方案来解决: dubbo支持telnet协议调用dubbo接口 dubbo的泛化调用可以在不引入提供方接口定义jar包的情况下对接口进行调用
258 0
使用dubbo-go搭建dubbo接口测试平台