This is an automated email from the ASF dual-hosted git repository.
baerwang pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go-pixiu.git
The following commit(s) were added to refs/heads/develop by this push:
new b39ccaad feat: added support for configcenter listening and logger
hot-reloading (#647)
b39ccaad is described below
commit b39ccaadd212812a2f3fcf76d84148c46e635c46
Author: mutezebra <[email protected]>
AuthorDate: Mon Jan 20 14:20:30 2025 +0800
feat: added support for configcenter listening and logger hot-reloading
(#647)
* feat: added support for configcenter listening and logger hot-reloading
* fix: fix license
* style: changed the format of import
* style: use go fmt
* fix: fix code
* style: add license
---
configcenter/configclient.go | 9 +-
configcenter/load.go | 28 +++--
configcenter/load_test.go | 7 +-
configcenter/nacos_load.go | 121 ++++++++++++---------
configcenter/nacos_load_test.go | 110 +++++++++++++++++++
configs/conf_with_nacos.yaml | 118 ++++++++++++++++++++
go.mod | 15 ++-
go.sum | 34 ++++--
pkg/cmd/gateway.go | 3 +
.../common/constant/hotreload.go | 14 +--
pkg/config/config_load.go | 13 ++-
pkg/hotreload/hotreload.go | 113 +++++++++++++++++++
pkg/hotreload/logger.go | 99 +++++++++++++++++
pkg/logger/controller.go | 4 +-
pkg/logger/logger.go | 5 +
pkg/model/log.go | 5 +-
16 files changed, 607 insertions(+), 91 deletions(-)
diff --git a/configcenter/configclient.go b/configcenter/configclient.go
index 37475ce4..e74f5710 100644
--- a/configcenter/configclient.go
+++ b/configcenter/configclient.go
@@ -17,12 +17,17 @@
package configcenter
+import (
+ "github.com/apache/dubbo-go-pixiu/pkg/model"
+)
+
type (
ConfigClient interface {
LoadConfig(properties map[string]interface{}) (string, error)
ListenConfig(properties map[string]interface{}) (err error)
- }
- ListenConfig func(data string)
+ // ViewConfig returns the current remote configuration.
+ ViewConfig() *model.Bootstrap
+ }
)
diff --git a/configcenter/load.go b/configcenter/load.go
index ab579adc..f16289c3 100644
--- a/configcenter/load.go
+++ b/configcenter/load.go
@@ -22,7 +22,7 @@ import (
)
import (
- "github.com/ghodss/yaml"
+ "gopkg.in/yaml.v3"
)
import (
@@ -42,6 +42,7 @@ var Parsers = map[string]func(data []byte, v interface{})
error{
type (
Load interface {
LoadConfigs(boot *model.Bootstrap, opts ...Option) (v
*model.Bootstrap, err error)
+ ViewRemoteConfig() *model.Bootstrap
}
Option func(opt *Options)
@@ -61,12 +62,14 @@ type DefaultConfigLoad struct {
}
func NewConfigLoad(bootConfig *model.Bootstrap) *DefaultConfigLoad {
-
var configClient ConfigClient
-
+ var err error
// config center load
if strings.EqualFold(bootConfig.Config.Type, KEY_CONFIG_TYPE_NACOS) {
- configClient, _ = NewNacosConfig(bootConfig)
+ configClient, err = NewNacosConfig(bootConfig)
+ if err != nil {
+ logger.Errorf("Get new nacos config failed,err: %v",
err)
+ }
}
if configClient == nil {
@@ -81,7 +84,6 @@ func NewConfigLoad(bootConfig *model.Bootstrap)
*DefaultConfigLoad {
}
func (d *DefaultConfigLoad) LoadConfigs(boot *model.Bootstrap, opts ...Option)
(v *model.Bootstrap, err error) {
-
var opt Options
for _, o := range opts {
o(&opt)
@@ -104,7 +106,6 @@ func (d *DefaultConfigLoad) LoadConfigs(boot
*model.Bootstrap, opts ...Option) (
}
data, err := d.configClient.LoadConfig(m)
-
if err != nil {
return nil, err
}
@@ -114,11 +115,24 @@ func (d *DefaultConfigLoad) LoadConfigs(boot
*model.Bootstrap, opts ...Option) (
return boot, err
}
- err = Parsers[".yml"]([]byte(data), boot)
+ if err = Parsers[".yml"]([]byte(data), boot); err != nil {
+ logger.Errorf("failed to parse the configuration loaded from
the remote,err: %v", err)
+ return boot, err
+ }
+
+ if err = d.configClient.ListenConfig(m); err != nil {
+ logger.Errorf("failed to listen the remote configcenter
config,err: %v", err)
+ return boot, err
+ }
return boot, err
}
+// ViewRemoteConfig returns the current remote configuration.
+func (d *DefaultConfigLoad) ViewRemoteConfig() *model.Bootstrap {
+ return d.configClient.ViewConfig()
+}
+
func ParseYamlBytes(content []byte, v interface{}) error {
return yaml.Unmarshal(content, v)
}
diff --git a/configcenter/load_test.go b/configcenter/load_test.go
index 292c1a8e..4fe85779 100644
--- a/configcenter/load_test.go
+++ b/configcenter/load_test.go
@@ -28,7 +28,6 @@ import (
)
func getBootstrap() *model.Bootstrap {
-
return &model.Bootstrap{
Config: &model.ConfigCenter{
Type: "nacos",
@@ -69,7 +68,7 @@ func TestDefaultConfigLoad_LoadConfigs(t *testing.T) {
boot *model.Bootstrap
opts []Option
}
- var tests = []struct {
+ tests := []struct {
name string
fields fields
args args
@@ -139,8 +138,8 @@ func TestDefaultConfigLoad_LoadConfigs(t *testing.T) {
t.Errorf("LoadConfigs() error = %v, wantErr
%v", err, tt.wantErr)
return
}
- //assert.True(t, gotV.Nacos.DataId == DataId, "load
config by nacos config center error!")
- //assert.True(t, len(gotV.StaticResources.Listeners) >
0, "load config by nacos config center error!")
+ // assert.True(t, gotV.Nacos.DataId == DataId, "load
config by nacos config center error!")
+ // assert.True(t, len(gotV.StaticResources.Listeners) >
0, "load config by nacos config center error!")
conf, _ := json.Marshal(gotV)
logger.Infof("config of Bootstrap load by nacos : %v",
string(conf))
})
diff --git a/configcenter/nacos_load.go b/configcenter/nacos_load.go
index 84681a8e..5f17857c 100644
--- a/configcenter/nacos_load.go
+++ b/configcenter/nacos_load.go
@@ -17,6 +17,10 @@
package configcenter
+import (
+ "sync"
+)
+
import (
"github.com/nacos-group/nacos-sdk-go/clients"
"github.com/nacos-group/nacos-sdk-go/clients/config_client"
@@ -30,6 +34,7 @@ import (
"github.com/apache/dubbo-go-pixiu/pkg/model"
)
+// Constants for configuration keys.
const (
KeyDataId = "dataId"
KeyGroup = "group"
@@ -39,6 +44,7 @@ const (
KeyTenant = "tenant"
)
+// Constants for Nacos configuration.
const (
DataId = "pixiu.yaml"
Group = "DEFAULT_GROUP"
@@ -50,29 +56,41 @@ const (
Scheme = "http"
)
-type (
- NacosConfig struct {
- client config_client.IConfigClient
+// NacosConfig represents the Nacos configuration client and its state.
+type NacosConfig struct {
+ client config_client.IConfigClient
+ remoteConfig *model.Bootstrap
+ mu sync.Mutex
+}
- // todo not support now
- listenConfigCallback ListenConfig
+// NewNacosConfig creates a new NacosConfig instance.
+// It returns an error if no Nacos server is configured or if the client
cannot be created.
+func NewNacosConfig(boot *model.Bootstrap) (ConfigClient, error) {
+ if len(boot.Nacos.ServerConfigs) == 0 {
+ return nil, errors.New("no Nacos server configured")
}
-)
-
-func NewNacosConfig(boot *model.Bootstrap) (configClient ConfigClient, err
error) {
- var sc []constant.ServerConfig
- if len(boot.Nacos.ServerConfigs) == 0 {
- return nil, errors.New("no nacos server be setted")
+ nacosClient, err := getNacosConfigClient(boot)
+ if err != nil {
+ return nil, err
}
- for _, serveConfig := range boot.Nacos.ServerConfigs {
- sc = append(sc, constant.ServerConfig{
- Port: serveConfig.Port,
- IpAddr: serveConfig.IpAddr,
+
+ return &NacosConfig{
+ client: nacosClient,
+ }, nil
+}
+
+// getNacosConfigClient initializes and returns a Nacos config client.
+func getNacosConfigClient(boot *model.Bootstrap) (config_client.IConfigClient,
error) {
+ var serverConfigs []constant.ServerConfig
+ for _, serverConfig := range boot.Nacos.ServerConfigs {
+ serverConfigs = append(serverConfigs, constant.ServerConfig{
+ Port: serverConfig.Port,
+ IpAddr: serverConfig.IpAddr,
})
}
- cc := constant.ClientConfig{
+ clientConfig := constant.ClientConfig{
NamespaceId: boot.Nacos.ClientConfig.NamespaceId,
TimeoutMs: boot.Nacos.ClientConfig.TimeoutMs,
NotLoadCacheAtStart:
boot.Nacos.ClientConfig.NotLoadCacheAtStart,
@@ -81,21 +99,15 @@ func NewNacosConfig(boot *model.Bootstrap) (configClient
ConfigClient, err error
LogLevel: boot.Nacos.ClientConfig.LogLevel,
}
- pa := vo.NacosClientParam{
- ClientConfig: &cc,
- ServerConfigs: sc,
- }
- nacos, err := clients.NewConfigClient(pa)
- if err != nil {
- return nil, err
- }
- configClient = &NacosConfig{
- client: nacos,
+ clientParam := vo.NacosClientParam{
+ ClientConfig: &clientConfig,
+ ServerConfigs: serverConfigs,
}
- return configClient, nil
+ return clients.NewConfigClient(clientParam)
}
+// LoadConfig retrieves the configuration from Nacos based on the provided
parameters.
func (n *NacosConfig) LoadConfig(param map[string]interface{}) (string, error)
{
return n.client.GetConfig(vo.ConfigParam{
DataId: getOrDefault(param[KeyDataId].(string), DataId),
@@ -103,34 +115,45 @@ func (n *NacosConfig) LoadConfig(param
map[string]interface{}) (string, error) {
})
}
-func getOrDefault(target string, quiet string) string {
+// getOrDefault returns the target value if it is not empty; otherwise, it
returns the fallback value.
+func getOrDefault(target, fallback string) string {
if len(target) == 0 {
- target = quiet
+ return fallback
}
return target
}
-func (n *NacosConfig) ListenConfig(param map[string]interface{}) (err error) {
- // todo noop, not support
- if true {
- return nil
- }
- listen := n.listen(getOrDefault(param[KeyDataId].(string), DataId),
getOrDefault(param[KeyGroup].(string), Group))
- return listen()
+// ListenConfig listens for configuration changes in Nacos.
+func (n *NacosConfig) ListenConfig(param map[string]interface{}) error {
+ return n.client.ListenConfig(vo.ConfigParam{
+ DataId: getOrDefault(param[KeyDataId].(string), DataId),
+ Group: getOrDefault(param[KeyGroup].(string), Group),
+ OnChange: n.onChange,
+ })
}
-func (n *NacosConfig) listen(dataId, group string) func() error {
- return func() error {
- return n.client.ListenConfig(vo.ConfigParam{
- DataId: dataId,
- Group: group,
- OnChange: func(namespace, group, dataId, data string) {
- if len(data) == 0 {
- logger.Errorf("nacos listen callback
data nil error , namespace : %s,group : %s , dataId : %s , data : %s")
- return
- }
- n.listenConfigCallback(data)
- },
- })
+// onChange is the callback function triggered when the configuration changes
in Nacos.
+func (n *NacosConfig) onChange(namespace, group, dataId, data string) {
+ if len(data) == 0 {
+ logger.Errorf("Nacos listen callback data is nil. Namespace:
%s, Group: %s, DataId: %s", namespace, group, dataId)
+ return
}
+
+ n.mu.Lock()
+ defer n.mu.Unlock()
+
+ var boot model.Bootstrap
+ if err := Parsers[".yml"]([]byte(data), &boot); err != nil {
+ logger.Errorf("Failed to parse the configuration loaded from
the remote. Error: %v", err)
+ return
+ }
+
+ n.remoteConfig = &boot
+}
+
+// ViewConfig returns the current remote configuration.
+func (n *NacosConfig) ViewConfig() *model.Bootstrap {
+ n.mu.Lock()
+ defer n.mu.Unlock()
+ return n.remoteConfig
}
diff --git a/configcenter/nacos_load_test.go b/configcenter/nacos_load_test.go
new file mode 100644
index 00000000..84ba7a33
--- /dev/null
+++ b/configcenter/nacos_load_test.go
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package configcenter
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "path"
+ "strings"
+ "testing"
+)
+
+import (
+ . "github.com/smartystreets/goconvey/convey"
+)
+
+import (
+ "github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+// isNacosRunning checks whether the Nacos server is running.
+// It returns true if Nacos is running, otherwise false.
+func isNacosRunning(t *testing.T) bool {
+ t.Helper()
+ _, err := getNacosConfigClient(getBootstrap())
+ return err == nil
+}
+
+// TestNewNacosConfig tests the creation of a new Nacos configuration.
+// If Nacos is not running, the test is skipped.
+func TestNewNacosConfig(t *testing.T) {
+ if !isNacosRunning(t) {
+ t.Skip("Nacos is not running, skipping the test.")
+ return
+ }
+
+ Convey("Test NewNacosConfig", t, func() {
+ cfg := getBootstrap()
+
+ // Test successful creation of NacosConfig.
+ _, err := NewNacosConfig(cfg)
+ So(err, ShouldBeNil)
+
+ // Test creation failure when Nacos server configurations are
missing.
+ cfg.Nacos.ServerConfigs = nil
+ _, err = NewNacosConfig(cfg)
+ So(err, ShouldNotBeNil)
+ })
+}
+
+// TestNacosConfig_onChange tests the onChange method of NacosConfig.
+func TestNacosConfig_onChange(t *testing.T) {
+ Convey("TestNacosConfig_onChange", t, func() {
+ cfg := getBootstrap()
+ c, err := NewNacosConfig(cfg)
+ So(err, ShouldBeNil)
+
+ client, ok := c.(*NacosConfig)
+ So(ok, ShouldBeTrue)
+
+ // Verify the current working directory.
+ wd, err := os.Getwd()
+ So(err, ShouldBeNil)
+
+ paths := strings.Split(wd, "/")
+ So(paths[len(paths)-1], ShouldEqual, "configcenter")
+
+ // Open the configuration file for testing.
+ file, err := os.Open(fmt.Sprintf("/%s/configs/conf.yaml",
path.Join(paths[:len(paths)-1]...)))
+ So(err, ShouldBeNil)
+ defer func() { So(file.Close(), ShouldBeNil) }()
+
+ conf, err := io.ReadAll(file)
+ So(err, ShouldBeNil)
+
+ Convey("Test onChange with valid input", func() {
+ So(client.remoteConfig, ShouldBeNil)
+ client.onChange(Namespace, Group, DataId, string(conf))
+ So(client.remoteConfig, ShouldNotBeNil)
+ })
+
+ Convey("Test onChange with empty input", func() {
+ // Suppress logs during this test.
+ logger.SetLoggerLevel("fatal")
+
+ client.remoteConfig = nil
+ client.onChange(Namespace, Group, DataId, "")
+ So(client.remoteConfig, ShouldBeNil)
+
+ // Restore the logger level.
+ logger.SetLoggerLevel("info")
+ })
+ })
+}
diff --git a/configs/conf_with_nacos.yaml b/configs/conf_with_nacos.yaml
new file mode 100644
index 00000000..46037e32
--- /dev/null
+++ b/configs/conf_with_nacos.yaml
@@ -0,0 +1,118 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+---
+static_resources:
+ listeners:
+ - name: "net/http"
+ protocol_type: "HTTP"
+ address:
+ socket_address:
+ address: "0.0.0.0"
+ port: 8888
+ filter_chains:
+ filters:
+ - name: dgp.filter.httpconnectionmanager
+ config:
+ route_config:
+ routes:
+ - match:
+ prefix: "/user"
+ route:
+ cluster: "user"
+ cluster_not_found_response_code: 505
+ http_filters:
+ - name: dgp.filter.http.httpproxy
+ config:
+ - name: dgp.filter.http.cors
+ config:
+ allow_origin:
+ - api.dubbo.com
+ allow_methods: ""
+ allow_headers: ""
+ expose_headers: ""
+ max_age: ""
+ allow_credentials: false
+ config:
+ idle_timeout: 5s
+ read_timeout: 5s
+ write_timeout: 5s
+ clusters:
+ - name: "user"
+ lb_policy: "lb"
+ endpoints:
+ - id: 1
+ socket_address:
+ address: 127.0.0.1
+ port: 1314
+ shutdown_config:
+ timeout: "60s"
+ step_timeout: "10s"
+ reject_policy: "immediacy"
+
+config-center:
+ type: "nacos"
+ enable: true
+
+nacos:
+ server_configs:
+ - ip_addr: "localhost"
+ port: 8848
+ scheme: "http"
+ contextPath: "/nacos"
+ - ip_addr: "localhost"
+ port: 8848
+ scheme: "http"
+ contextPath: "/nacos"
+ client-config:
+ cache_dir: "./.cache"
+ log_dir: "./.log"
+ not_load_cache_at_start: true
+ namespace_id: "dubbo-go-pixiu"
+ data-id: "pixiu.yaml"
+ group: "DEFAULT_GROUP"
+
+log:
+ level: "debug"
+ development: true
+ disableCaller: false
+ disableStacktrace: false
+ sampling:
+ encoding: "console"
+
+ # encoder
+ encoderConfig:
+ messageKey: "message"
+ levelKey: "level"
+ timeKey: "time"
+ nameKey: "logger"
+ callerKey: "caller"
+ stacktraceKey: "stacktrace"
+ lineEnding: ""
+ levelEncoder: "capitalColor"
+ timeEncoder: "iso8601"
+ durationEncoder: "seconds"
+ callerEncoder: "short"
+ nameEncoder: ""
+
+ outputPaths:
+ - "stderr"
+ errorOutputPaths:
+ - "stderr"
+ initialFields:
+
diff --git a/go.mod b/go.mod
index e02699fc..f526fb03 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,6 @@ require (
github.com/dubbogo/grpc-go v1.42.10
github.com/dubbogo/triple v1.2.2-rc3
github.com/envoyproxy/go-control-plane
v0.11.1-0.20230524094728-9239064ad72f
- github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
github.com/go-errors/errors v1.0.1
github.com/go-playground/assert/v2 v2.2.0
github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9
@@ -31,6 +30,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.14.0
github.com/prometheus/common v0.37.0
+ github.com/smartystreets/goconvey v1.7.2
github.com/spf13/cast v1.5.0
github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.4
@@ -50,6 +50,7 @@ require (
google.golang.org/grpc v1.56.3
google.golang.org/protobuf v1.30.0
gopkg.in/yaml.v2 v2.4.0
+ gopkg.in/yaml.v3 v3.0.1
mosn.io/proxy-wasm-go-host v0.1.0
)
@@ -89,6 +90,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.3.0 // indirect
+ github.com/gopherjs/gopherjs v1.12.80 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 // indirect
github.com/grpc-ecosystem/grpc-opentracing
v0.0.0-20180507213350-8e809c8a8645 // indirect
@@ -107,6 +109,7 @@ require (
github.com/jinzhu/copier v0.3.5 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
+ github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/k0kubun/pp v3.0.1+incompatible // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/knadh/koanf v1.5.0 // indirect
@@ -137,6 +140,7 @@ require (
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 //
indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/shirou/gopsutil/v3 v3.22.2 // indirect
+ github.com/smartystreets/assertions v1.2.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
@@ -155,15 +159,14 @@ require (
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
- golang.org/x/arch v0.0.0-20200826200359-b19915210f00 // indirect
+ golang.org/x/arch v0.11.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
- golang.org/x/sync v0.1.0 // indirect
- golang.org/x/sys v0.13.0 // indirect
- golang.org/x/text v0.13.0 // indirect
+ golang.org/x/sync v0.7.0 // indirect
+ golang.org/x/sys v0.26.0 // indirect
+ golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.1.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 //
indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index c1ac65ec..1bfae555 100644
--- a/go.sum
+++ b/go.sum
@@ -614,8 +614,6 @@ github.com/fsnotify/fsnotify v1.6.0
h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4
github.com/fsnotify/fsnotify v1.6.0/go.mod
h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/getsentry/raven-go v0.2.0/go.mod
h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v1.0.0/go.mod
h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
-github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod
h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod
h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-co-op/gocron v1.9.0
h1:+V+DDenw3ryB7B+tK1bAIC5p0ruw4oX9IqAsdRnGIf0=
github.com/go-co-op/gocron v1.9.0/go.mod
h1:DbJm9kdgr1sEvWpHCA7dFFs/PGHPMil9/97EXCRPr4k=
@@ -786,8 +784,9 @@ github.com/googleapis/gax-go/v2 v2.7.0/go.mod
h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57Q
github.com/googleapis/go-type-adapters v1.0.0/go.mod
h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing
v0.0.0-20200911160855-bcd43fbb19e8/go.mod
h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod
h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99
h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod
h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v1.12.80
h1:aC68NT6VK715WeUapxcPSFq/a3gZdS32HdtghdOIgAo=
+github.com/gopherjs/gopherjs v1.12.80/go.mod
h1:d55Q4EjGQHeJVms+9LGtXul6ykz5Xzx1E1gaXQXdimY=
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod
h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
github.com/gorilla/context v1.1.1/go.mod
h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod
h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@@ -1055,6 +1054,8 @@ github.com/nats-io/nats.go v1.9.1/go.mod
h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
github.com/nats-io/nkeys v0.1.0/go.mod
h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod
h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod
h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod
h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
+github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod
h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod
h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod
h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso=
github.com/npillmayer/nestext v0.1.3/go.mod
h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk=
@@ -1175,6 +1176,7 @@ github.com/robfig/cron/v3 v3.0.1
h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod
h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod
h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod
h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rogpeppe/go-internal v1.0.1-alpha.1/go.mod
h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod
h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod
h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0
h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
@@ -1191,18 +1193,23 @@ github.com/shirou/gopsutil v3.20.11+incompatible/go.mod
h1:5b4v6he4MtMOwMlS0TUMT
github.com/shirou/gopsutil/v3 v3.21.6/go.mod
h1:JfVbDpIBLVzT8oKbvMg9P3wEIMDDpVn+LwHTKj0ST88=
github.com/shirou/gopsutil/v3 v3.22.2
h1:wCrArWFkHYIdDxx/FSfF5RB4dpJYW6t7rcp3+zL8uks=
github.com/shirou/gopsutil/v3 v3.22.2/go.mod
h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtSoSBy0L5vUHY=
+github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod
h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
+github.com/shurcooL/httpfs v0.0.0-20181222201310-74dc9339e414/go.mod
h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod
h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/vfsgen v0.0.0-20180915214035-33ae1944be3f/go.mod
h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/sirupsen/logrus v1.2.0/go.mod
h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod
h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod
h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod
h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1
h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod
h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d
h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod
h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/assertions v1.2.0
h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
+github.com/smartystreets/assertions v1.2.0/go.mod
h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod
h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/smartystreets/goconvey v1.6.4
h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod
h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/smartystreets/goconvey v1.7.2
h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
+github.com/smartystreets/goconvey v1.7.2/go.mod
h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
github.com/soheilhy/cmux v0.1.4/go.mod
h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5
h1:GJTW+uNMIV1RKwox+T4aN0/sQlYRg78uHZf2H0aBcDw=
github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5/go.mod
h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
@@ -1382,8 +1389,10 @@ go.uber.org/zap v1.16.0/go.mod
h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
-golang.org/x/arch v0.0.0-20200826200359-b19915210f00
h1:cfd5G6xu8iZTFmjBYVemyBmE/sTf0A3vpE3BmoOuLCI=
golang.org/x/arch v0.0.0-20200826200359-b19915210f00/go.mod
h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
+golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
+golang.org/x/arch v0.11.0/go.mod
h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
+golang.org/x/crypto v0.0.0-20180807104621-f027049dab0a/go.mod
h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod
h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod
h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -1565,8 +1574,10 @@ golang.org/x/sync
v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20180807162357-acbc56fc7007/go.mod
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1678,8 +1689,8 @@ golang.org/x/sys v0.2.0/go.mod
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
+golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod
h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod
h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -1701,8 +1712,8 @@ golang.org/x/text v0.4.0/go.mod
h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
-golang.org/x/text v0.13.0/go.mod
h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod
h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod
h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1721,6 +1732,7 @@ golang.org/x/tools
v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod
h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190308142131-b40df0fb21c3/go.mod
h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod
h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod
h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod
h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
diff --git a/pkg/cmd/gateway.go b/pkg/cmd/gateway.go
index 9dc4ff69..4392d29b 100644
--- a/pkg/cmd/gateway.go
+++ b/pkg/cmd/gateway.go
@@ -32,6 +32,7 @@ import (
"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
pxruntime "github.com/apache/dubbo-go-pixiu/pkg/common/runtime"
"github.com/apache/dubbo-go-pixiu/pkg/config"
+ "github.com/apache/dubbo-go-pixiu/pkg/hotreload"
"github.com/apache/dubbo-go-pixiu/pkg/logger"
"github.com/apache/dubbo-go-pixiu/pkg/model"
"github.com/apache/dubbo-go-pixiu/pkg/server"
@@ -125,6 +126,8 @@ func (d *DefaultDeployer) initialize() error {
logger.Errorf("[startCmd] failed to get limit cpu number, %s",
err.Error())
}
+ hotreload.StartHotReload(d.configManger, d.bootstrap)
+
return err
}
diff --git a/configcenter/configclient.go b/pkg/common/constant/hotreload.go
similarity index 78%
copy from configcenter/configclient.go
copy to pkg/common/constant/hotreload.go
index 37475ce4..9d0d7072 100644
--- a/configcenter/configclient.go
+++ b/pkg/common/constant/hotreload.go
@@ -15,14 +15,12 @@
* limitations under the License.
*/
-package configcenter
+package constant
-type (
- ConfigClient interface {
- LoadConfig(properties map[string]interface{}) (string, error)
-
- ListenConfig(properties map[string]interface{}) (err error)
- }
+import (
+ "time"
+)
- ListenConfig func(data string)
+const (
+ CheckConfigInterval = 1 * time.Second
)
diff --git a/pkg/config/config_load.go b/pkg/config/config_load.go
index a004a77d..25fcae13 100644
--- a/pkg/config/config_load.go
+++ b/pkg/config/config_load.go
@@ -27,9 +27,9 @@ import (
import (
"github.com/creasty/defaults"
- "github.com/ghodss/yaml"
"github.com/goinggo/mapstructure"
"github.com/imdario/mergo"
+ "gopkg.in/yaml.v3"
)
import (
@@ -84,7 +84,7 @@ func CheckYamlFormat(path string) bool {
// LoadYAMLConfig YAMLConfigLoad config load yaml
func LoadYAMLConfig(path string) *model.Bootstrap {
- log.Println("load config in YAML format from : ", path)
+ logger.Info("load config in YAML format from : ", path)
content, err := os.ReadFile(path)
if err != nil {
log.Fatalln("[config] [yaml load] load config failed, ", err)
@@ -253,6 +253,15 @@ func (m *ConfigManager) loadRemoteBootConfigs()
*model.Bootstrap {
return configs
}
+// ViewRemoteConfig returns the current remote configuration.
+func (m *ConfigManager) ViewRemoteConfig() *model.Bootstrap {
+ if m.load == nil {
+ return nil
+ }
+
+ return m.load.ViewRemoteConfig()
+}
+
func (m *ConfigManager) check() error {
return Adapter(config)
diff --git a/pkg/hotreload/hotreload.go b/pkg/hotreload/hotreload.go
new file mode 100644
index 00000000..cd6acce5
--- /dev/null
+++ b/pkg/hotreload/hotreload.go
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package hotreload
+
+import (
+ "sync"
+ "time"
+)
+
+import (
+ "github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+ "github.com/apache/dubbo-go-pixiu/pkg/config"
+ "github.com/apache/dubbo-go-pixiu/pkg/logger"
+ "github.com/apache/dubbo-go-pixiu/pkg/model"
+)
+
+// HotReloader defines the interface for the HotReload module.
+type HotReloader interface {
+ // CheckUpdate checks if the configuration has changed and needs to be
reloaded.
+ CheckUpdate(oldConfig, newConfig *model.Bootstrap) bool
+
+ // HotReload performs the hot reload of the configuration.
+ HotReload(oldConfig, newConfig *model.Bootstrap) error
+}
+
+// Coordinator listens for configuration file changes and notifies registered
reloaders
+// to perform hot reload when the configuration changes.
+type Coordinator struct {
+ reloaders []HotReloader // List of registered reloaders
+ boot *model.Bootstrap // Current configuration
+ manager *config.ConfigManager // Configuration manager
+}
+
+var coordinator = Coordinator{reloaders: []HotReloader{&LoggerReloader{}}}
+
+// StartHotReload initializes the hot reload process.
+// It should be called when the project starts, e.g., in cmd/gateway.go.
+func StartHotReload(manager *config.ConfigManager, boot *model.Bootstrap) {
+ if manager == nil || boot == nil {
+ logger.Warn("ConfigManager or Bootstrap is nil, hot reload will
not start")
+ return
+ }
+
+ coordinator.manager = manager
+ coordinator.boot = boot
+ go coordinator.HotReload()
+}
+
+// HotReload periodically checks for configuration updates and triggers hot
reload if changes are detected.
+func (c *Coordinator) HotReload() {
+ for {
+ time.Sleep(constant.CheckConfigInterval)
+
+ boot := c.manager.ViewRemoteConfig()
+ if boot == nil {
+ continue
+ }
+
+ c.hotReload(boot)
+ }
+}
+
+// hotReload checks for configuration changes and triggers hot reload for
registered reloaders.
+func (c *Coordinator) hotReload(newBoot *model.Bootstrap) {
+ changed := false
+ wg := &sync.WaitGroup{}
+
+ for _, reloader := range c.reloaders {
+ if reloader.CheckUpdate(c.boot, newBoot) {
+ changed = true
+ wg.Add(1)
+ go func(r HotReloader) {
+ defer wg.Done()
+ if err := r.HotReload(c.boot, newBoot); err !=
nil {
+ logger.Errorf("Hot reload failed: %v",
err)
+ }
+ }(reloader)
+ }
+ }
+
+ wg.Wait()
+ if changed {
+ c.boot = newBoot
+ }
+}
+
+// equal checks if two string slices are equal.
+func equal(s1, s2 []string) bool {
+ if len(s1) != len(s2) {
+ return false
+ }
+ for i := range s1 {
+ if s1[i] != s2[i] {
+ return false
+ }
+ }
+ return true
+}
diff --git a/pkg/hotreload/logger.go b/pkg/hotreload/logger.go
new file mode 100644
index 00000000..45430cf3
--- /dev/null
+++ b/pkg/hotreload/logger.go
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package hotreload
+
+import (
+ "github.com/apache/dubbo-go-pixiu/pkg/logger"
+ "github.com/apache/dubbo-go-pixiu/pkg/model"
+)
+
+// LoggerReloader implements the HotReloader interface for reloading logger
configurations.
+type LoggerReloader struct{}
+
+// CheckUpdate compares the old and new logger configurations to determine if
a reload is needed.
+func (r *LoggerReloader) CheckUpdate(oldConfig, newConfig *model.Bootstrap)
bool {
+ oc := oldConfig.Log
+ nc := newConfig.Log
+
+ if oc == nil && nc != nil {
+ return true
+ }
+
+ if oc != nil && nc == nil {
+ return false
+ }
+
+ // Check if any logger configuration fields have changed.
+ if oc.Level != nc.Level ||
+ oc.Development != nc.Development ||
+ oc.DisableCaller != nc.DisableCaller ||
+ oc.DisableStacktrace != nc.DisableStacktrace ||
+ oc.Encoding != nc.Encoding {
+ return false
+ }
+
+ // Check sampling configuration.
+ if !r.checkSampling(oc.Sampling, nc.Sampling) {
+ return false
+ }
+
+ // Check encoder configuration.
+ if !r.checkEncoderConfig(oc.EncoderConfig, nc.EncoderConfig) {
+ return false
+ }
+
+ // Check output paths.
+ if !equal(oc.OutputPaths, nc.OutputPaths) {
+ return false
+ }
+
+ return true
+}
+
+// HotReload applies the new logger configuration.
+func (r *LoggerReloader) HotReload(oldConfig, newConfig *model.Bootstrap)
error {
+ if err := logger.HotReload(newConfig.Log.Build()); err != nil {
+ logger.Errorf("Failed to reload logger configuration: %v", err)
+ return err
+ }
+ return nil
+}
+
+// checkSampling compares the old and new sampling configurations.
+func (r *LoggerReloader) checkSampling(oldSampling, newSampling
model.SamplingConfig) bool {
+ return oldSampling.Initial == newSampling.Initial &&
oldSampling.Thereafter == newSampling.Thereafter
+}
+
+// checkEncoderConfig compares the old and new encoder configurations.
+func (r *LoggerReloader) checkEncoderConfig(oldEncoderConfig, newEncoderConfig
model.EncoderConfig) bool {
+ return oldEncoderConfig.MessageKey == newEncoderConfig.MessageKey &&
+ oldEncoderConfig.LevelKey == newEncoderConfig.LevelKey &&
+ oldEncoderConfig.TimeKey == newEncoderConfig.TimeKey &&
+ oldEncoderConfig.NameKey == newEncoderConfig.NameKey &&
+ oldEncoderConfig.CallerKey == newEncoderConfig.CallerKey &&
+ oldEncoderConfig.FunctionKey == newEncoderConfig.FunctionKey &&
+ oldEncoderConfig.StacktraceKey ==
newEncoderConfig.StacktraceKey &&
+ oldEncoderConfig.SkipLineEnding ==
newEncoderConfig.SkipLineEnding &&
+ oldEncoderConfig.LineEnding == newEncoderConfig.LineEnding &&
+ oldEncoderConfig.EncodeLevel == newEncoderConfig.EncodeLevel &&
+ oldEncoderConfig.EncodeTime == newEncoderConfig.EncodeTime &&
+ oldEncoderConfig.EncodeDuration ==
newEncoderConfig.EncodeDuration &&
+ oldEncoderConfig.EncodeCaller == newEncoderConfig.EncodeCaller
&&
+ oldEncoderConfig.EncodeName == newEncoderConfig.EncodeName &&
+ oldEncoderConfig.ConsoleSeparator ==
newEncoderConfig.ConsoleSeparator
+}
diff --git a/pkg/logger/controller.go b/pkg/logger/controller.go
index ce015429..91449361 100644
--- a/pkg/logger/controller.go
+++ b/pkg/logger/controller.go
@@ -20,7 +20,9 @@ package logger
import (
"strings"
"sync"
+)
+import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
@@ -42,7 +44,7 @@ func (c *logController) setLoggerLevel(level string) bool {
}
c.logger.config.Level = *lvl
- l, _ := c.logger.config.Build()
+ l, _ := c.logger.config.Build(zap.AddCallerSkip(2))
c.logger = &logger{SugaredLogger: l.Sugar(), config: c.logger.config}
return true
}
diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go
index 9cbb49d2..9fd73af1 100644
--- a/pkg/logger/logger.go
+++ b/pkg/logger/logger.go
@@ -107,3 +107,8 @@ func InitLogger(conf *zap.Config) {
func SetLoggerLevel(level string) bool {
return control.setLoggerLevel(level)
}
+
+func HotReload(conf *zap.Config) error {
+ InitLogger(conf)
+ return nil
+}
diff --git a/pkg/model/log.go b/pkg/model/log.go
index 60bb5185..16a8c74f 100644
--- a/pkg/model/log.go
+++ b/pkg/model/log.go
@@ -18,11 +18,14 @@
package model
import (
- "github.com/apache/dubbo-go-pixiu/pkg/logger"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
+import (
+ "github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
type Log struct {
Level string `json:"level" yaml:"level"`
Development bool `json:"development"
yaml:"development"`