From f37d9bea6abc757c845784dbc0e0f5e67a6ac510 Mon Sep 17 00:00:00 2001 From: liuxingwang Date: Fri, 12 Jul 2019 11:41:24 +0800 Subject: [PATCH] =?UTF-8?q?[feature]=201.=20=E5=A2=9E=E5=8A=A0=E4=BA=86?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E9=80=9A=E7=9F=A5=E4=B8=AD=E8=AF=A6=E7=BB=86?= =?UTF-8?q?=E7=9A=84=E6=95=B0=E6=8D=AE=E4=BF=AE=E6=94=B9=E9=83=A8=E5=88=86?= =?UTF-8?q?=202.=20=E4=BF=AE=E6=AD=A3=E4=BA=86preload=E7=9A=84namespace?= =?UTF-8?q?=E4=B9=9F=E4=BC=9A=E6=9C=89=E4=BA=8B=E4=BB=B6=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agollo.go | 27 ++++++++++++++----------- change.go | 32 +++++++++++++++++++++++++++++ configurations.go | 42 ++++++++++++++++++++++++++++++++++++++ configurations_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 change.go create mode 100644 configurations.go create mode 100644 configurations_test.go diff --git a/agollo.go b/agollo.go index 2c30cd8..d214410 100644 --- a/agollo.go +++ b/agollo.go @@ -36,12 +36,11 @@ type Agollo interface { Options() Options } -type Configurations map[string]interface{} - type ApolloResponse struct { Namespace string OldValue Configurations NewValue Configurations + Changes Changes Error error } @@ -117,7 +116,8 @@ func NewWithConfigFile(configFilePath string, opts ...Option) (Agollo, error) { func (a *agollo) preload() (Agollo, error) { for _, namespace := range a.opts.PreloadNamespaces { - _, err := a.loadConfigFromNonCache(namespace) + // The action do not need to notify + _, err := a.loadConfigFromNonCache(namespace, false) if err != nil { if a.opts.FailTolerantOnBackupExists { _, err = a.loadBackup(namespace) @@ -167,7 +167,7 @@ func (a *agollo) loadNameSpace(namespace string) Configurations { a.notificationMap.LoadOrStore(namespace, defaultNotificationID) if a.opts.AutoFetchOnCacheMiss { - configs, err := a.loadConfigFromCache(namespace) + configs, err := a.loadConfigFromCache(namespace, true) if err == nil { a.log("Namesapce", namespace, "From", "cache-api") return configs @@ -215,6 +215,7 @@ func (a *agollo) sendWatchCh(namespace string, oldVal, newVal Configurations) { Namespace: namespace, OldValue: oldVal, NewValue: newVal, + Changes: oldVal.Different(newVal), } timer := time.NewTimer(defaultWatchTimeout) @@ -268,7 +269,7 @@ func (a *agollo) log(kvs ...interface{}) { ) } -func (a *agollo) loadConfigFromCache(namespace string) (configurations Configurations, err error) { +func (a *agollo) loadConfigFromCache(namespace string, isNeedNotify bool) (configurations Configurations, err error) { configurations, err = a.opts.ApolloClient.GetConfigsFromCache( a.opts.ConfigServerURL, a.opts.AppID, @@ -279,12 +280,12 @@ func (a *agollo) loadConfigFromCache(namespace string) (configurations Configura return } - err = a.handleConfig(namespace, configurations) + err = a.handleConfig(namespace, configurations, isNeedNotify) return } -func (a *agollo) loadConfigFromNonCache(namespace string) (configurations Configurations, err error) { +func (a *agollo) loadConfigFromNonCache(namespace string, isNeedNotify bool) (configurations Configurations, err error) { var ( status int @@ -306,21 +307,23 @@ func (a *agollo) loadConfigFromNonCache(namespace string) (configurations Config if status == http.StatusOK { configurations = config.Configurations a.namespaceMap.Store(namespace, config.ReleaseKey) - err = a.handleConfig(namespace, config.Configurations) + err = a.handleConfig(namespace, config.Configurations, isNeedNotify) return } return } -func (a *agollo) handleConfig(namespace string, configurations Configurations) error { +func (a *agollo) handleConfig(namespace string, configurations Configurations, isNeedNotify bool) error { // 读取旧缓存用来给监听队列 oldValue := a.GetNameSpace(namespace) // 覆盖旧缓存 a.cache.Store(namespace, configurations) - // 发送到监听channel - a.sendWatchCh(namespace, oldValue, configurations) + if isNeedNotify { + // 发送到监听channel + a.sendWatchCh(namespace, oldValue, configurations) + } // 备份配置 return a.backup() } @@ -385,7 +388,7 @@ func (a *agollo) longPoll() { if status == http.StatusOK { // 服务端判断没有改变,不会返回结果,这个时候不需要修改,遍历空数组跳过 for _, notification := range notifications { - _, err = a.loadConfigFromNonCache(notification.NamespaceName) + _, err = a.loadConfigFromNonCache(notification.NamespaceName, true) if err == nil { a.notificationMap.Store(notification.NamespaceName, notification.NotificationID) continue diff --git a/change.go b/change.go new file mode 100644 index 0000000..57643da --- /dev/null +++ b/change.go @@ -0,0 +1,32 @@ +package agollo + +type ChangeType string + +const ( + ChangeTypeAdd ChangeType = "add" + ChangeTypeUpdate ChangeType = "update" + ChangeTypeDelete ChangeType = "delete" +) + +type Change struct { + Type ChangeType + Key string + Value interface{} +} + +type Changes []Change + +// Len is part of sort.Interface. +func (cs Changes) Len() int { + return len(cs) +} + +// Swap is part of sort.Interface. +func (cs Changes) Swap(i, j int) { + cs[i], cs[j] = cs[j], cs[i] +} + +// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. +func (cs Changes) Less(i, j int) bool { + return cs[i].Key < cs[j].Key +} diff --git a/configurations.go b/configurations.go new file mode 100644 index 0000000..0ae01ed --- /dev/null +++ b/configurations.go @@ -0,0 +1,42 @@ +package agollo + +import "sort" + +type Configurations map[string]interface{} + +func (old Configurations) Different(new Configurations) Changes { + var changes []Change + for k, newValue := range new { + oldValue, found := old[k] + if found { + if oldValue != newValue { + changes = append(changes, Change{ + Type: ChangeTypeUpdate, + Key: k, + Value: newValue, + }) + } + } else { + changes = append(changes, Change{ + Type: ChangeTypeAdd, + Key: k, + Value: newValue, + }) + } + } + + for k, oldValue := range old { + _, found := new[k] + if !found { + changes = append(changes, Change{ + Type: ChangeTypeDelete, + Key: k, + Value: oldValue, + }) + } + } + + sort.Sort(Changes(changes)) + + return changes +} diff --git a/configurations_test.go b/configurations_test.go new file mode 100644 index 0000000..cd3f9f8 --- /dev/null +++ b/configurations_test.go @@ -0,0 +1,46 @@ +package agollo + +import "testing" + +func TestConfigurationsDifferent(t *testing.T) { + tests := []struct { + old Configurations + new Configurations + changes []Change + }{ + { + Configurations{ + "name": "foo", + "age": 18, + "balance": 101.2, + }, + + Configurations{ + "name": "foo", + "age": 19, + "height": 1.82, + }, + []Change{ + Change{ChangeTypeUpdate, "age", 19}, + Change{ChangeTypeDelete, "balance", 101.2}, + Change{ChangeTypeAdd, "height", 1.82}, + }, + }, + } + + for _, test := range tests { + changes := test.old.Different(test.new) + + if len(changes) != len(test.changes) { + t.Errorf("should be equal (expected=%v, actual=%v)", test.changes, len(changes)) + } + for i, actual := range changes { + expected := test.changes[i] + if actual.Type != expected.Type && + actual.Key != expected.Key && + actual.Value != expected.Value { + t.Errorf("should be equal (expected=%v, actual=%v)", expected, actual) + } + } + } +}