Giter Site home page Giter Site logo

agollo's Introduction

Agollo - Go Client for Apollo

Build Status Go Report Card GolangCI codebeat badge golang GoDoc GitHub release License

携程Apollo Golang版客户端

针对apollo openapi的golang 客户端封装

快速开始

获取安装

go get -u github.com/shima-park/agollo

Features

  • 实时同步配置,配置改动监听
  • 配置文件容灾
  • 支持多namespace, cluster
  • 客户端SLB
  • 提供Viper配置库的apollo插件
  • 支持通过 ACCESSKEY_SECRET 来实现 client 安全访问
  • 支持自定义签名认证

示例

读取配置

此示例场景适用于程序启动时读取一次。不会额外启动goroutine同步配置

package main

import (
	"fmt"

	"github.com/shima-park/agollo"
)

func main() {
	a, err := agollo.New("localhost:8080", "your_appid", agollo.AutoFetchOnCacheMiss())
	if err != nil {
		panic(err)
	}

	fmt.Println(
		// 默认读取Namespace:application下key: foo的value
		a.Get("foo"),

		// 获取namespace为test.json的所有配置项
		a.GetNameSpace("test.json"),

		// 当key:foo不存在时,提供一个默认值bar
		a.Get("foo", agollo.WithDefault("bar")),

		// 读取Namespace为other_namespace, key: foo的value
		a.Get("foo", agollo.WithNamespace("other_namespace")),
	)
}

实时同步配置

启动一个goroutine实时同步配置, errorCh返回notifications/v2非httpcode(200)的错误信息

a, err := agollo.New("localhost:8080", "your_appid", agollo.PreloadNamespaces("application", "test.json"))
// error handle...

errorCh := a.Start()  // Start后会启动goroutine监听变化,并更新agollo对象内的配置cache
// 或者忽略错误处理直接 a.Start()

配置监听

监听所有namespace配置变更事件

a, err := agollo.New("localhost:8080", "your_appid", agollo.PreloadNamespaces("application", "test.json"))
// error handle...

errorCh := a.Start()  // Start后会启动goroutine监听变化,并更新agollo对象内的配置cache
// 或者忽略错误处理直接 a.Start()

watchCh := a.Watch()

for{
	select{
	case err := <- errorCh:
		// handle error
	case resp := <-watchCh:
		fmt.Println(
			"Namespace:", resp.Namespace,
			"OldValue:", resp.OldValue,
			"NewValue:", resp.NewValue,
			"Error:", resp.Error,
		)
	}
}

配置文件容灾

初始化时增加agollo.FailTolerantOnBackupExists()即可, 在连接apollo失败时,如果在配置的目录下存在.agollo备份配置,会读取备份在服务器无法连接的情况下

a, err := agollo.New("localhost:8080", "your_appid",
		agollo.FailTolerantOnBackupExists(),
		// other options...
	)
// error handle...

支持多namespace

初始化时增加agollo.AutoFetchOnCacheMiss() 当本地缓存中namespace不存在时,尝试去apollo缓存接口去获取

a, err := agollo.New("localhost:8080", "your_appid",
		agollo.AutoFetchOnCacheMiss(),
		// other options...
	)
// error handle...

appNS, aNS, bNS := a.GetNameSpace("application"), a.GetNameSpace("Namespace_A"), a.GetNameSpace("Namespace_B")

a.Get("foo") // 默认从application这个namespace中查找配置项
a.Get("foo", agollo.WithNamespace("Namespace_A")) // 从Namespace_A中获取配置项foo
a.Get("foo", agollo.WithNamespace("Namespace_B")) // 从Namespace_B中获取配置项foo
// ...

或者初始化时增加agollo.PreloadNamespaces("Namespace_A", "Namespace_B", ...)预加载这几个Namespace的配置

a, err := agollo.New("localhost:8080", "your_appid",
		agollo.PreloadNamespaces("Namespace_A", "Namespace_B", ...),
		// other options...
	)
// error handle...

当然两者结合使用也是可以的。

a, err := agollo.New("localhost:8080", "your_appid",
		agollo.PreloadNamespaces("Namespace_A", "Namespace_B", ...),
		agollo.AutoFetchOnCacheMiss(),
		// other options...
	)
// error handle...

如何支持多cluster

初始化时增加agollo.Cluster("your_cluster"),并创建多个Agollo接口实例issue

cluster_a, err := agollo.New("localhost:8080", "your_appid",
		agollo.Cluster("cluster_a"),
		agollo.AutoFetchOnCacheMiss(),
		// other options...
	)

cluster_b, err := agollo.New("localhost:8080", "your_appid",
		agollo.Cluster("cluster_b"),
		agollo.AutoFetchOnCacheMiss(),
		// other options...
	)

cluster_a.Get("foo")
cluster_b.Get("foo")
// ...

客户端SLB

客户端通过MetaServer进行动态SLB的启用逻辑:

//方式1:
    // 使用者主动增加配置项agollo.EnableSLB(true)
    a, err := agollo.New("localhost:8080", "your_appid", agollo.EnableSLB(true))


//方式2:
    // (客户端显示传递的configServerURL) 和 (环境变量中的APOLLO_CONFIGSERVICE) 都为空值
    // export APOLLO_CONFIGSERVICE=""
    // 此方式必须设置 export APOLLO_META="your meta_server address"
    a, err := agollo.New("", "your_appid")

客户端静态SLB(现在支持","分割的多个configServer地址列表):

//方式1:
    // 直接传入","分割的多个configServer地址列表
    a, err := agollo.New("localhost:8080,localhost:8081,localhost:8082", "your_appid")

//方式2:
    // 在环境变量中APOLLO_CONFIGSERVICE设置","分割的多个configServer地址列表
    // export APOLLO_CONFIGSERVICE="localhost:8080,localhost:8081,localhost:8082"
    a, err := agollo.New("", "your_appid")

SLB更新间隔默认是60s和官方java sdk保持一致,可以通过agollo.ConfigServerRefreshIntervalInSecond(time.Second * 90)来修改

    a, err := agollo.New("localhost:8080", "your_appid",
        agollo.EnableSLB(true),
        agollo.ConfigServerRefreshIntervalInSecond(time.Second * 90),
    )

! SLB的MetaServer地址来源(用来调用接口获取configServer列表),取下列表中非空的一项:

  1. 用户显示传递的configServerURL
  2. 环境变量中的APOLLO_META

! SLB的默认采用的算法是RoundRobin

初始化方式

三种package级别初始化,影响默认对象和package提供的静态方法。适用于不做对象传递,单一AppID的场景

// 读取当前目录下app.properties,适用于原始apollo定义的读取固定配置文件同学
agollo.InitWithDefaultConfigFile(opts ...Option) error

agollo.Init(configServerURL, appID string, opts ...Option) (err error)

agollo.InitWithConfigFile(configFilePath string, opts ...Option) (err error)

两种新建对象初始化方法。返回独立的Agollo接口对象。互相之间不会影响,适用于多AppID,Cluser, ConfigServer配置读取 issue

agollo.New(configServerURL, appID string, opts ...Option) (Agollo, error)
agollo.NewWithConfigFile(configFilePath string, opts ...Option) (Agollo, error)

初始化时可选配置项

更多配置请见options.go

        // 打印日志,打印日志注入有效的io.Writer,默认: ioutil.Discard
	agollo.WithLogger(agollo.NewLogger(agollo.LoggerWriter(os.Stdout))),

	// 默认的集群名称,默认:default
	agollo.Cluster(cluster),

	// 预先加载的namespace列表,如果是通过配置启动,会在app.properties配置的基础上追加
	agollo.PreloadNamespaces("Namespace_A", "Namespace_B", ...),

	// 在配置未找到时,去apollo的带缓存的获取配置接口,获取配置
	agollo.AutoFetchOnCacheMiss(),

	// 备份文件存放地址,默认:当前目录下/.agollo,一般结合FailTolerantOnBackupExists使用
	agollo.BackupFile("/tmp/xxx/.agollo")
	// 在连接apollo失败时,如果在配置的目录下存在.agollo备份配置,会读取备份在服务器无法连接的情况下
	agollo.FailTolerantOnBackupExists(),

详细特性展示

请将example/sample下app.properties修改为你本地或者测试的apollo配置。 示例代码

结合viper使用,提高配置读取舒适度

例如apollo中有以下配置:

appsalt = xxx
database.driver = mysql
database.host = localhost
database.port = 3306
database.timeout = 5s
// ...

示例代码:

import (
    "fmt"
	"github.com/shima-park/agollo/viper-remote"
	"github.com/spf13/viper"
)

type Config struct {
	AppSalt string         `mapstructure:"appsalt"`
	DB      DatabaseConfig `mapstructure:"database"`
}

type DatabaseConfig struct {
	Driver   string        `mapstructure:"driver"`
	Host     string        `mapstructure:"host"`
	Port     int           `mapstructure:"port"`
	Timeout time.Duration  `mapstructure:"timeout"`
	// ...
}

func main(){
    remote.SetAppID("your_appid")
    v := viper.New()
    v.SetConfigType("prop") // 根据namespace实际格式设置对应type
    err := v.AddRemoteProvider("apollo", "your_apollo_endpoint", "your_apollo_namespace")
    // error handle...
    err = v.ReadRemoteConfig()
    // error handle...

    // 直接反序列化到结构体中
    var conf Config
    err = v.Unmarshal(&conf)
    // error handle...
    fmt.Printf("%+v\n", conf)

    // 各种基础类型配置项读取
    fmt.Println("Host:", v.GetString("db.host"))
    fmt.Println("Port:", v.GetInt("db.port"))
    fmt.Println("Timeout:", v.GetDuration("db.timeout"))

    // 获取所有key,所有配置
    fmt.Println("AllKeys", v.AllKeys(), "AllSettings",  v.AllSettings())
}

viper配置同步

基于轮训的配置同步

    remote.SetAppID("your_appid")
    v := viper.New()
    v.SetConfigType("prop")
    err := v.AddRemoteProvider("apollo", "your_apollo_endpoint", "your_apollo_namespace")
    // error handle...
    err = v.ReadRemoteConfig()
    // error handle...

    for {
	time.Sleep(10 * time.Second)

	err := v.WatchRemoteConfig() // 每次调用该方法,会从apollo缓存接口获取一次配置,并更新viper
	if err != nil {
		panic(err)
	}

	fmt.Println("AllSettings:", v.AllSettings())
     }

基于事件监听配置同步

    remote.SetAppID("your_appid")
    v := viper.New()
    v.SetConfigType("prop")
    err := v.AddRemoteProvider("apollo", "your_apollo_endpoint", "your_apollo_namespace")
    // error handle...
    err = v.ReadRemoteConfig()
    // error handle...

    v.WatchRemoteConfigOnChannel() // 启动一个goroutine来同步配置更改

    for {
	time.Sleep(1 * time.Second)
	fmt.Println("AllSettings:", v.AllSettings())
     }

License

The project is licensed under the Apache 2 license.

agollo's People

Contributors

647-coder avatar hugeskull avatar lujiajing1126 avatar shima-park avatar shinejstar avatar wuzuoliang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

agollo's Issues

客户端负载均衡

点赞作者,简单的核心API留足了扩展能力,很棒的一个库~

Apollo架构上将Config、Meta、Eureka打在同一进程来做服务发现,咱们是否考虑利用MetaServer做客户端负载均衡,进一步提高可用性(对齐Java客户端)

accidentally panic

Hi, I'm using viper-remote and it will occasionally panic and stack print as bellow.
image

And I go on the code. I think type assertions may fix this panic.

请教如果同时监听多个cluster需要怎么处理?

配置监听多个cluster,但只有当最后一个cluster 有变更生效才会监听到~~

func main() {

initConfig("ini", "goApollo.ini")

for _, cluster := range appConfig.Clusters {
// 数组

	err := agollo.Init(
		appConfig.ConfigServerUrl,
		appConfig.AppId,
		agollo.Cluster(cluster),
		agollo.PreloadNamespaces(appConfig.NamespaceName),
		agollo.AutoFetchOnCacheMiss(),
		agollo.FailTolerantOnBackupExists(),
		agollo.WithLogger(agollo.NewLogger(agollo.LoggerWriter(os.Stdout))),
	)
	if err != nil {
		panic(err)
	}


	file, err := os.Open(cluster)
	if err != nil && os.IsNotExist(err) {
		file, _ = os.Create(cluster)
		defer file.Close()
	}

	fount, err := os.OpenFile(cluster, os.O_TRUNC|os.O_WRONLY, 0644)
	if err != nil {
		panic(err)
	}
	//defer fount.Close()
	res := agollo.GetNameSpace(appConfig.NamespaceName)

	for k, v := range res {
		data := fmt.Sprintf("%s=%s\n", k, v)
		//fmt.Println(data)
		if _, err := fount.WriteString(data); err != nil {
			panic(err)
		}
	}
	fmt.Printf("%s write Success\n",cluster)

}
errorCh := agollo.Start()
watchCh := agollo.Watch()
go func() {
for {
select {
case err := <-errorCh:
fmt.Println("Error:", err)
case update := <-watchCh:
fmt.Println("Apollo Update:", update.NewValue)
fmt.Println(reflect.TypeOf(update))
for _, cluster := range appConfig.Clusters {
//writeNamespaceInfo(cluster)
do something
}
//default:
// fmt.Println("Apollo not update")
}
}
}()
select {}
}

配置监听 这个demo的 Namespace 拼错了

    // ....
    for {
		select {
		case err := <-errorCh:
			fmt.Println(err)
		case resp := <-watchCh:
			fmt.Println(
				"Namesapce:", resp.Namesapce, // 这里是 Namespace
				"OldValue:", resp.OldValue,
				"NewValue:", resp.NewValue,
				"Error:", resp.Error,
			)
		}
	}

使用预加载namespace的疑问

使用预加载一个namespace,启动期间没有配置发生变更,但依然在监听配置变更收到变更.
下面是打印的日志

[Agollo]  ConfigServerUrl http://192.168.36.175:28080 AppID test Cluster default Namesapce Kafka From cache
Watch Apollo: &{Kafka map[brokers:192.168.36.175:9020,192.168.36.176:9020 version:1.1.0] <nil>}

要怎么避免这种情况?

如何获取yaml文件的配置呢?

hi, 您好,我在apollo后台配置yaml文件类型的配置, 在客户端 .SetConfigType("yaml") 读不到配置
是不是我写法不对?

go mod tidy 报错

        github.com/shima-park/agollo/viper-remote imports
        github.com/xordataexchange/crypt/config imports
        github.com/xordataexchange/crypt/backend/etcd imports
        github.com/coreos/etcd/client tested by
        github.com/coreos/etcd/client.test imports
        github.com/coreos/etcd/integration imports
        github.com/coreos/etcd/proxy/grpcproxy imports
        google.golang.org/grpc/naming: module google.golang.org/grpc@latest found (v1.37.0), but does not contain package google.golang.org/grpc/naming

agollo v1.2.8, 启用从备份文件加载时, 在调用GetNameSpace(key)正常时, 会将备份文件的其他namespace的数据删除

重现场景

  • 1.本地已存在具有所有namespace数据的备份文件

  • 2.伪代码如下

a, _ := agollo.New("http://localhost:8080", "app",
    agollo.AutoFetchOnCacheMiss(),
    agollo.Cluster("default"),,
    agollo.FailTolerantOnBackupExists(),
)
a.GetNameSpace("test") // 正常调用
// 此时会删除备份文件的其它namespace数据并写入test的配置数据
a.GetNameSpace("test2") // 此时网络故障, 得到空的配置结果
// 在这里理论上应该能从备份文件中获取配置数据, 但是这个数据在上一步被删掉了

在配置未找到时,去apollo的带缓存的获取配置接口,获取配置

这个选项有点奇怪,缓存接口并不是这么用。带缓存配置接口用途,是用于配置不敏感的应用,按一个特定的周期,循环获取,不需要notificaiton。比如每30秒更新一遍。

不知道这个AutoFetchOnCacheMiss选项的意图是什么?

用于当程序Get了一个app.properties不存在的namespace?

apollo的meta地址必须非http得BUG

url := fmt.Sprintf("http://%s/configs/%s/%s/%s?releaseKey=%s&ip=%s", configServerURL, url.QueryEscape(appID), url.QueryEscape(cluster), url.QueryEscape(c.getNamespace(namespace)), options.ReleaseKey, c.IP, )
这里不需要直接制定一个http

agollo.GetNameSpace中调用initNamespace返回的错误希望可以传递出来

func (a *agollo) GetNameSpace(namespace string) Configurations {
	config, found := a.cache.LoadOrStore(namespace, Configurations{})
	if !found && a.opts.AutoFetchOnCacheMiss {
		err := a.initNamespace(namespace)
		if err != nil {
			a.log("Action", "InitNamespace", "Error", err)
		}
		return a.getNameSpace(namespace)
	}

	return config.(Configurations)
}

这里initNamespace之后的err只是打了log,是不是可以返回出来呢?我遇到的场景是不小心配置错了apollo地址,然后第一次服务启动去读apollo配置时被重定向到了登录页,因为http.Client中自动处理了重定向,导致拿到了200的响应,但是读不到配置,最后返回了空配置。

我尝试使用agollo.WithDoer替换掉默认http client然后增加CheckRedirect,但是抛出的错误在上述位置没有返回出来。希望可以返回出来,这样在启动时就可以判断有没有正确获取到配置。

返回数据格式异常

我apollo的数据是这样的:

appsalt = some appsalt

database.driver = mysql
database.host = mysql
database.port = 3306
database.user = root
database.password = 123456
database.dbname = chat

然后我的 struct 是这样定义的

type Config struct {
	AppSalt      string           `json:"appsalt"`
	DB           DatabaseConfig   `json:"database"`
        ...
}
type DatabaseConfig struct {
	Driver   string `json:"driver"`
	Host     string `json:"host"`
	Port     string `json:"port"`
	User     string `json:"user"`
	Password string `json:"password"`
	DBName   string `json:"dbname"`
}

apollo获取到的数据是这样的

map[appsalt:testaaaaa database.dbname:chat database.driver:mysql database.host:mysql database.password:123456 database.port:3306 database.user:root]

我怎么才能把获取到的这种数据映射到我的 struct中去呢?

start、watch 无法监听配置

agolloClient, err := agollo.New(
"xx",
"xx",
agollo.BackupFile(backupFile),
agollo.AutoFetchOnCacheMiss(),
agollo.PreloadNamespaces("xx.json"),
)
errChan := agolloClient.Start()
watchChan := agolloClient.Watch()
ticker := time.NewTicker(1 * time.Second)
for {
select {
case <-ticker.C:
ret := agolloClient.GetNameSpace("xx.json")
base.Test_debug(ret, 0)
case err := <-errChan:
base.Test_debug(err, 0)
case resp := <-watchChan:
base.Test_debug(resp, 0)
}

为什么只在启动时打印出了一次xx.json的值,后续打印都是空。任何修改变动,都没有任何打印。

配置监听的 case 有问题

原来的代码里面,没有注册通知 ID,会导致通知收不到

a, err := agollo.New("localhost:8080", "your_appid", agollo.AutoFetchOnCacheMiss())
// error handle...

errorCh := a.Start()  // Start后会启动goroutine监听变化,并更新agollo对象内的配置cache
// 或者忽略错误处理直接 a.Start()

// ** 这里需要注册通知ID **

watchCh := a.Watch()
// ...

修改后:

a, err := agollo.New("localhost:8080", "your_appid", agollo.AutoFetchOnCacheMiss())
// error handle...

errorCh := a.Start()  // Start后会启动goroutine监听变化,并更新agollo对象内的配置cache
// 或者忽略错误处理直接 a.Start()

// 这里需要注册通知ID
a.GetNameSpace("application")
a.GetNameSpace("test.json")

watchCh := a.Watch()
// ...

[BUG] WatchRemoteConfigOnChannel is not working

It can not detect the changes, codes:

package main

import (
	"fmt"
	"log"
	"time"

	remote "github.com/shima-park/agollo/viper-remote"
	"github.com/spf13/viper"
)

func main() {
	remote.SetAppID("vip-service")
	namespaces := []string{"application", "database"}

	vipers := make(map[string]*viper.Viper)
	for _, ns := range namespaces {
		v := viper.New()
		v.SetConfigType("prop")
		err := v.AddRemoteProvider("apollo", "localhost:8080", ns)
		if err != nil {
			log.Fatal(err)
		}
		err = v.ReadRemoteConfig()
		if err != nil {
			log.Fatal(err)
		}

		v.WatchRemoteConfigOnChannel() // not working

		vipers[ns] = v
	}

	for {
		for ns, v := range vipers {
			fmt.Println(ns, v.AllSettings()) // every time gets the same configs
		}
		time.Sleep(5 * time.Second)
	}
}

It works if I switch to use WatchRemoteConfig:

for {
	for ns, v := range vipers {
		err := v.WatchRemoteConfig()
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(ns, v.AllSettings())
	}
	time.Sleep(5 * time.Second)
}

agollo如何与viper接合实现监听

请问agollo如何与viper接合实现监听,我调用viper.WatchRemoteConfig方法后,发现从viper读出来的值一直没有变化,而根目录下的.agollo文件却实时更新了。

[BUG] longpolling无限循环

新建client时假如option中没有默认填入namespace application会自动添加到local storage中

	if len(options.PreloadNamespaces) == 0 {
		options.PreloadNamespaces = []string{defaultNamespace}
	} else {
		if !stringInSlice(defaultNamespace, options.PreloadNamespaces) {
			PreloadNamespaces(defaultNamespace)(&options)
		}
	}

如果使用longpolling接口,并且所监听的app-cluster中没有发布过"application" namespace会导致客户端按照默认的每秒访问一次 longpolling的几个相关接口

数组问题

如果配置文件中有数组形式,使用viper没有解析出来,请问这种情况该咋操作
image

关于配置自动更新的问题

你好。我在代码中测试apollo的设置的时候,我使用了这里例子里的自动更新。但两种模式中,自动更新的方法,打印的结果始终是服务启动时候的内容,不会随着我在apollo界面的更改而更改。而10秒轮询的方法,可以成功访问到改了的和增加的数据,但是如果我删除了某个字段,在代码中仍然会打印该字段原先的值。
请问是需要什么额外的配置么?

ticker 取数据,仅第一次能取到值

`

func main() {
ch := make(chan string)
a, err := agollo.New("localhost:8080",
"hdy", agollo.Cluster("hdy-cluster"),
agollo.DefaultNamespace("hdy.dev"),
agollo.AutoFetchOnCacheMiss())
if err != nil {
panic(err)
}
a.Start()
d := time.Duration(time.Second * 2)
t := time.NewTicker(d)
defer t.Stop()
go func() {
for {
<-t.C
fmt.Println(
"user_max_count",a.Get("user_max_count",agollo.WithDefault("defaultValue")),
)
}
}()
ch <- ""
}

`

输出

user_max_count 3000000

user_max_count defaultValue

在不改配置的情况的,第一次能取到值,后面都是defaultValue
在Apollo页面改一次user_max_count的值,就能再取到数据不再是defaultValue

go的初学者,也是第一次用Apollo,请问这个事如何解决?

namespace 首次创建,客户端监听不到

客户端启动监听某个namespace(尚未创建),当 namespace 创建时,客户端并未监听到,只能在 namespace 创建好后,更新配置才能被监听。望修复,谢谢。

不能监听所有 namespace 配置的变更

按照样例,不能监听所有 namespace 配置变更,从代码上看,创建客户端时,如果未填写 namespace 将会使用默认的 namespace,因此只能监听默认 namespace 的变更,这块能修复下吗?

在remote中添加option的cluster参数不生效

我用viper来远程获取Apollo配置中心参数,使用了remote.SetAgolloOptions(agollo.Cluster("test"))添加了cluster信息,debug源码后发现请求的url地址都正确,但无法返回正确的配置信息,请问能给点建议吗

Action LoadConfigFromNonCache Error Get

错误提示
[Agollo] ConfigServerUrl http://192.168.36.175:28080 AppID cscloud-test Cluster default Namespace application Action LoadConfigFromNonCache Error Get http://192.168.36.175:28080/configs/cscloud-test/default/application?releaseKey=&ip=xxx.xxx.xxx.xxx: EOF
但是直接访问是可以的
image
您看看有可能是什么原因导致的?

封装设计学习了,想问多应用多集群的问题

想问一下……什么场景会出现多应用和多集群的监听

按目前的设计,agollo只对应1个应用1个集群,是很常规的设定;

我看到了已经closed issue,所以会好奇,到底需不需要把这部分需求也考虑进去设计。

提一些问题和建议

首先我个人想表扬作者,这个 agollo 实现不知道比官方推荐前两个实现好多少倍。前两个实现在我看来实在是太烂,过度的设计导致可读性和实用性变得极差。作者写简单和直接,也是 golang 官方倡导的。
其次我想提一些问题和建议:

  1. Start 和 Stop 方法可以用 Context 的形式来做,就像 etcd 一样,这种模式是一种比较通用的模式,理解成本低
  2. Watch 机制说实话我有点晕,我到现在也没跑通。我第一次启动 Watch 了配置竟然没有获取到配置,可否详细阐述下各种情况下 Watch 是怎么生效的?(可能和最近一次的提交有关)

Start()后会把所有的数据置为空

    c, err := agollo.New("http://127.0.0.1:8080", "test",
        agollo.AutoFetchOnCacheMiss(),
        //  agollo.FailTolerantOnBackupExists(),  // 关闭容灾
    )
    failOnError(err, "初始化失败")
    defer c.Stop()

    _ = c.Start()

    func() {
        for {
            time.Sleep(1e9)
            fmt.Println("a:", c.Get("a"), "b:", c.Get("b"))
        }
    }()

结果如下:
a: 1 b: 2
a: b:
a: b:
a: b:
a: b:
a: b:
...

请问环境env该如何设置

请问一下env可以设置嘛,比如我在fat和pro环境中有同样名字的集群,我该怎么区分它们呐

能否为ApolloClient提供自定义签名入口

需求说明

提供一个选项用于自定义签名方法

原因

apollo目前不支持全局token或支持不友好, 所以我前面挂了一个nginx, 在nginx上添加基础认证.
我可以通过WithDoer拦截修改req的Header方式来迂回的解决他, 但是我想要更好的方式, 谢谢!

docker环境下或者多网卡情况获取ip地址问题

应该是考虑到网络设置因人而异,apollo 通过ApolloClientOption选项允许用户自主设置客户端ip,但是在结合viper使用的时候内部均屏蔽了这些选项,统一采用默认配置,导致在docker环境下或者多网卡等复杂情况获取ip地址不准确,是否可以提供接口允许用户设置一些全局配置项,比如客户端ip?

使用 viper-remote 会报错

error trace:

github.com/coreos/etcd/client

../../../../pkg/mod/github.com/coreos/[email protected]+incompatible/client/keys.generated.go:63:14: z.HasExtensions undefined (type codec.genHelperEncoder has no field or method HasExtensions)
../../../../pkg/mod/github.com/coreos/[email protected]+incompatible/client/keys.generated.go:63:35: z.EncExt undefined (type codec.genHelperEncoder has no field or method EncExt)
../../../../pkg/mod/github.com/coreos/[email protected]+incompatible/client/keys.generated.go:75:6: r.WriteArrayElem undefined (type codec.genHelperEncDriver has no field or method WriteArrayElem)
../../../../pkg/mod/github.com/coreos/[email protected]+incompatible/client/keys.generated.go:83:6: r.WriteMapElemKey undefined (type codec.genHelperEncDriver has no field or method WriteMapElemKey)
../../../../pkg/mod/github.com/coreos/[email protected]+incompatible/client/keys.generated.go:84:6: r.EncodeString undefined (type codec.genHelperEncDriver has no field or method EncodeString)
../../../../pkg/mod/github.com/coreos/[email protected]+incompatible/client/keys.generated.go:85:6: r.WriteMapElemValue undefined (type codec.genHelperEncDriver has no field or method WriteMapElemValue)
../../../../pkg/mod/github.com/coreos/[email protected]+incompatible/client/keys.generated.go:94:6: r.WriteArrayElem undefined (type codec.genHelperEncDriver has no field or method WriteArrayElem)
../../../../pkg/mod/github.com/coreos/[email protected]+incompatible/client/keys.generated.go:99:7: r.EncodeString undefined (type codec.genHelperEncDriver has no field or method EncodeString)
../../../../pkg/mod/github.com/coreos/[email protected]+incompatible/client/keys.generated.go:102:6: r.WriteMapElemKey undefined (type codec.genHelperEncDriver has no field or method WriteMapElemKey)
../../../../pkg/mod/github.com/coreos/[email protected]+incompatible/client/keys.generated.go:103:6: r.EncodeString undefined (type codec.genHelperEncDriver has no field or method EncodeString)
../../../../pkg/mod/github.com/coreos/[email protected]+incompatible/client/keys.generated.go:103:6: too many errors

关于监听配置变更的建议

感谢你的开源。
我在使用过程中发现无论是使用agollo.Watch()还是agollo.WatchNamespace(),配置变更时返回的ApolloResponse中OldValue与NewValue除了变更的部分还包括了未变更的,这给我带来了一些不便,因为我还要对比OldValue与NewValue两个map找出两者的不同,感觉有点多余,我希望的是OldValue与NewValue只有变更的部分。
这是我打印的ApolloResponse,其中m_db是变更的部分:

&{Mysql
map[m_db:testdb m_host:localhost m_password:123456 m_port:3307 m_username:root] 
map[m_db:testdb1 m_host:localhost m_password:123456 m_port:3307 m_username:root] 
<nil>
}

viper方式多namespace支持问题

目前是v.AddRemoteProvider("apollo", Endpoint, NameSpace)
namespace只支持传入一个, 如果同一个app下有多个yaml或者json的配置文件 ,是否可以一次性获取多个namespace下的配置?

我看viper的AddRemoteProvider 是支持多次加入的, 放到了 v.remoteProviders ,获取的时候ReadRemoteConfig 也是支持循环remoteProviders的

如果项目支持 namespace传入切片就很棒了

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.