kratos配置

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 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及使用

相关文章
|
7月前
|
Go
golang安装protoc和gRPC步骤
golang安装protoc和gRPC步骤
165 0
|
消息中间件 存储 Go
Golang微服务框架Kratos应用NSQ消息队列
NSQ是一个基于Go语言的分布式实时消息平台,它基于MIT开源协议发布,由bitly公司开源出来的一款简单易用的消息中间件。 NSQ可用于大规模系统中的实时消息服务,并且每天能够处理数亿级别的消息,其设计目标是为在分布式环境下运行的去中心化服务提供一个强大的基础架构。 NSQ具有分布式、去中心化的拓扑结构,该结构具有无单点故障、故障容错、高可用性以及能够保证消息的可靠传递的特征。NSQ非常容易配置和部署,且具有最大的灵活性,支持众多消息协议。
107 1
|
消息中间件 Go 流计算
Golang微服务框架Kratos应用NATS消息队列详解
Golang微服务框架Kratos应用NATS消息队列详解
388 1
|
消息中间件 存储 Go
Golang微服务框架Kratos应用Pulsar消息队列
Apache Pulsar 是 Apache 软件基金会的顶级项目,是下一代云原生分布式消息流平台,集消息、存储、轻量化函数式计算为一体,采用计算与存储分离架构设计,支持多租户、持久化存储、多机房跨区域数据复制,具有强一致性、高吞吐、低延时及高可扩展性等流数据存储特性。
137 1
|
API Go 网络架构
Kratos 大乱炖 —— 整合其他Web框架:Gin、FastHttp、Hertz
Kratos默认的RPC框架使用的是gRPC,支持REST和protobuf两种通讯协议。其API都是使用protobuf定义的,REST协议是通过[grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway)转译实现的。使用protobuf定义API是具有极大优点的,具有很强的可读性、可维护性,以及工程性。工程再大,人员再多,也不会乱。 一切看起来都是很美好的。那么,问题来了,我们现在使用的是其他的Web框架,迁移就会有成本,有风险,不可能一下子就把历史存在的代码一口气转换过来到Kratos框架。那我可以在Kratos中整合其他
901 0
|
编译器 Shell Go
|
开发框架 JavaScript .NET
Golang微服务框架kratos实现SignalR服务
SignalR 在可用的情况下使用新的 WebSocket 传输,并在必要时回退到旧传输。 虽然当然可以直接使用 WebSocket 编写应用,但使用 SignalR 意味着需要实现的许多额外功能已经为你完成。 最重要的是,这意味着你可以编写应用代码以利用 WebSocket,而无需担心为旧客户端创建单独的代码路径。 SignalR 还可以避免担心 WebSocket 的更新,因为 SignalR 已更新以支持基础传输中的更改,从而为应用程序提供跨 WebSocket 版本的一致接口。
76 0
|
机器学习/深度学习 前端开发 Go
Golang微服务框架kratos实现SSE服务
我也是最近才知道SSE的,问了下周围的人,发现知道的人也着实不多的。我是怎么知道SSE的呢?我看了下OpenAI的API,有一个Stream模式,就是使用的SSE实现的。说白了,这就是一个HTTP长连接通过服务端持续发送数据到前端的协议。在网络不稳定的情况下,它比Websocket要更好。
295 0
|
Go API 微服务
Golang微服务框架Kratos轻松集成并使用Swagger UI
在我们的开发当中,调试接口,测试接口,提供接口文档给前端,那都是非常频繁的工作内容。 那么,我们需要用什么方法和工具来实施这些工作内容呢? Swagger,或者说OpenAPI。
301 0
|
消息中间件 Go 网络性能优化
Golang微服务框架Kratos应用NATS消息队列
NATS是由CloudFoundry的架构师Derek开发的一个开源的、轻量级、高性能的,支持发布、订阅机制的分布式消息队列系统。它的核心基于EventMachine开发,代码量不多,可以下载下来慢慢研究。其核心原理就是基于消息发布订阅机制。每个台服务 器上的每个模块会根据自己的消息类别,向MessageBus发布多个消息主题;而同时也向自己需要交互的模块,按照需要的信息内容的消息主题订阅消息。 NATS原来是使用Ruby编写,可以实现每秒150k消息,后来使用Go语言重写,能够达到每秒8-11百万个消息,整个程序很小只有3M Docker image
175 0