GithubHelp home page GithubHelp logo

shima-park / agollo Goto Github PK

View Code? Open in Web Editor NEW
282.0 6.0 50.0 139 KB

An elegant Go client for Ctrip Apollo

License: Apache License 2.0

Go 100.00%
apollo-golang golang ctrip apollo apollo-client viper

agollo's Issues

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

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

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

[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.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,但是抛出的错误在上述位置没有返回出来。希望可以返回出来,这样在启动时就可以判断有没有正确获取到配置。

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") // 此时网络故障, 得到空的配置结果
// 在这里理论上应该能从备份文件中获取配置数据, 但是这个数据在上一步被删掉了

客户端负载均衡

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

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

返回数据格式异常

我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中去呢?

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
您看看有可能是什么原因导致的?

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

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

使用预加载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>}

要怎么避免这种情况?

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

需求说明

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

原因

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

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

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

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

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

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:
...

数组问题

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

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,请问这个事如何解决?

配置监听的 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()
// ...

viper方式多namespace支持问题

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

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

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

[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的几个相关接口

关于配置自动更新的问题

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

如何获取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 实现不知道比官方推荐前两个实现好多少倍。前两个实现在我看来实在是太烂,过度的设计导致可读性和实用性变得极差。作者写简单和直接,也是 golang 官方倡导的。
其次我想提一些问题和建议:

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

请问环境env该如何设置

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

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的值,后续打印都是空。任何修改变动,都没有任何打印。

关于监听配置变更的建议

感谢你的开源。
我在使用过程中发现无论是使用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>
}

请教如果同时监听多个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 {}
}

agollo如何与viper接合实现监听

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

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

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

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.

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

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

配置监听 这个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,
			)
		}
	}

使用 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只对应1个应用1个集群,是很常规的设定;

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

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.