From f58002f8f2e7531f5d733a34089ce772df8e0a4a Mon Sep 17 00:00:00 2001 From: lmk <1095689763@qq.com> Date: Fri, 10 Apr 2026 15:54:39 +0800 Subject: [PATCH] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E4=BB=8Eco?= =?UTF-8?q?nsul=E7=9A=84=E9=85=8D=E7=BD=AE=E4=B8=AD=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=EF=BC=8C=E7=9B=91=E5=90=AC=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=94=B9=E5=8F=98=E7=9A=84=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.go | 16 ----- consul/consul.go | 162 +++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 6 +- go.sum | 8 +++ 4 files changed, 175 insertions(+), 17 deletions(-) delete mode 100644 config/config.go diff --git a/config/config.go b/config/config.go deleted file mode 100644 index 67bf1f0..0000000 --- a/config/config.go +++ /dev/null @@ -1,16 +0,0 @@ -package config - -import ( - "fmt" - - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gcfg" - "github.com/gogf/gf/v2/os/genv" -) - -func init() { - env := genv.Get("APP_ENV", "").String() - if env != "" { - g.Cfg().GetAdapter().(*gcfg.AdapterFile).SetFileName(fmt.Sprintf("config-%s.yml", env)) - } -} diff --git a/consul/consul.go b/consul/consul.go index c0a4ee0..42c1dfa 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -13,7 +13,11 @@ import ( "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/gsel" "github.com/gogf/gf/v2/net/gsvc" + "github.com/gogf/gf/v2/os/gcfg" + "github.com/gogf/gf/v2/os/genv" "github.com/gogf/gf/v2/util/grand" + "github.com/hashicorp/consul/api" + "github.com/r3labs/diff/v2" ) var ( @@ -135,7 +139,165 @@ func init() { // 连接成功后启动健康检查和自动重连 go startHealthCheckAndReconnect() } + + loadConfigFromConsul() } + +func loadConfigFromConsul() { + ctx := context.Background() + + serviceName := g.Cfg().MustGet(ctx, "server.name", "admin-go").String() + env := genv.Get("APP_ENV", "").String() + fmt.Printf("服务名称: %s, 环境变量 APP_ENV: %s\n", serviceName, env) + + if env == "" { + fmt.Printf("未设置 APP_ENV 环境变量,跳过 Consul 配置加载\n") + return + } + + consulData, lastIndex, err := loadEnvFromConsul(env, serviceName) + if err == nil && len(consulData) > 0 { + adapter, err := gcfg.NewAdapterContent() + if err != nil { + fmt.Printf("创建配置适配器失败: %v\n", err) + return + } + adapter.SetContent(string(consulData)) + g.Cfg().SetAdapter(adapter) + + fmt.Printf("已从 Consul 成功加载初始配置\n") + + go watchConsulConfig(env, serviceName, lastIndex) + } else { + fmt.Printf("从 Consul 获取配置失败,使用本地配置文件\n") + } +} + +func loadEnvFromConsul(env string, serviceName string) ([]byte, uint64, error) { + ctx := context.Background() + + consulAddress := g.Cfg().MustGet(ctx, "consul.address", "127.0.0.1:8500").String() + fmt.Printf("Consul 地址: %s\n", consulAddress) + + config := api.DefaultConfig() + config.Address = consulAddress + + client, err := api.NewClient(config) + if err != nil { + fmt.Printf("创建 Consul 客户端失败: %v\n", err) + return nil, 0, err + } + consulKey := fmt.Sprintf("config/%s/%s-%s", serviceName, serviceName, env) + fmt.Printf("从 Consul 读取配置键: %s\n", consulKey) + + kv := client.KV() + + opts := &api.QueryOptions{ + WaitIndex: 0, + WaitTime: 60 * time.Second, + } + + pair, meta, err := kv.Get(consulKey, opts) + if err != nil { + fmt.Printf("从 Consul 读取配置失败: %v\n", err) + return nil, 0, err + } + + if pair == nil { + fmt.Printf("Consul 中未找到配置键: %s\n", consulKey) + return nil, 0, nil + } + + fmt.Printf("已从 Consul 加载配置, Index: %d\n", meta.LastIndex) + return pair.Value, meta.LastIndex, nil +} + +func watchConsulConfig(env string, serviceName string, lastIndex uint64) { + ctx := context.Background() + + consulAddress := g.Cfg().MustGet(ctx, "consul.address", "127.0.0.1:8500").String() + config := api.DefaultConfig() + config.Address = consulAddress + + client, err := api.NewClient(config) + if err != nil { + fmt.Printf("创建 Consul 监听客户端失败: %v\n", err) + return + } + + consulKey := fmt.Sprintf("config/%s/%s-%s", serviceName, serviceName, env) + + for { + opts := &api.QueryOptions{ + WaitIndex: lastIndex, + WaitTime: 60 * time.Second, + } + + pair, meta, err := client.KV().Get(consulKey, opts) + if err != nil { + fmt.Printf("Consul 监听出错: %v, 5秒后重试...\n", err) + time.Sleep(5 * time.Second) + continue + } + + if meta.LastIndex == lastIndex { + continue + } + + if pair == nil { + fmt.Printf("Consul 配置被删除: %s\n", consulKey) + lastIndex = meta.LastIndex + continue + } + + fmt.Printf("检测到 Consul 配置变更: %s, New Index: %d\n", consulKey, meta.LastIndex) + + updateLocalConfig(pair.Value) + + lastIndex = meta.LastIndex + } +} + +func updateLocalConfig(content []byte) { + ctx := context.Background() + + oldConfig, _ := g.Cfg().Data(ctx) + + adapter, err := gcfg.NewAdapterContent() + if err != nil { + fmt.Printf("创建新配置适配器失败: %v\n", err) + return + } + adapter.SetContent(string(content)) + g.Cfg().SetAdapter(adapter) + + newConfig, _ := g.Cfg().Data(ctx) + + changelog, err := diff.Diff(oldConfig, newConfig) + if err != nil { + fmt.Printf("配置对比失败: %v\n", err) + return + } + + if len(changelog) == 0 { + fmt.Printf("配置已热更新成功(内容无实质变化)\n") + return + } + + fmt.Printf("=== 检测到配置变更 (%d 项) ===\n", len(changelog)) + for _, change := range changelog { + switch change.Type { + case "create": + fmt.Printf("[+] 新增: %s = %v\n", change.Path, change.To) + case "update": + fmt.Printf("[~] 修改: %s: %v -> %v\n", change.Path, change.From, change.To) + case "delete": + fmt.Printf("[-] 删除: %s (原值: %v)\n", change.Path, change.From) + } + } + fmt.Printf("=================================\n") +} + func getLocalIP() (string, error) { // 获取本机所有网络接口 addrs, err := net.InterfaceAddrs() diff --git a/go.mod b/go.mod index b86c7e3..81d2c40 100644 --- a/go.mod +++ b/go.mod @@ -10,11 +10,14 @@ require ( github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5 github.com/gogf/gf/v2 v2.9.5 github.com/google/uuid v1.6.0 + github.com/hashicorp/consul/api v1.26.1 github.com/meilisearch/meilisearch-go v0.36.1 github.com/minio/minio-go/v7 v7.0.97 github.com/nats-io/nats.go v1.48.0 github.com/olivere/elastic/v7 v7.0.32 + github.com/r3labs/diff/v2 v2.15.1 github.com/rabbitmq/amqp091-go v1.10.0 + github.com/rogpeppe/go-internal v1.13.1 github.com/rpcxio/rpcx-consul v0.1.1 github.com/smallnest/rpcx v1.9.1 github.com/tiger1103/gfast-token v1.0.10 @@ -67,7 +70,6 @@ require ( github.com/grandcat/zeroconf v1.0.0 // indirect github.com/grokify/html-strip-tags-go v0.1.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect - github.com/hashicorp/consul/api v1.26.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect @@ -127,6 +129,7 @@ require ( github.com/tklauser/numcpus v0.2.2 // indirect github.com/valyala/fastrand v1.1.0 // indirect github.com/vcaesar/cedar v0.30.0 // indirect + github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect @@ -150,6 +153,7 @@ require ( golang.org/x/sys v0.35.0 // indirect golang.org/x/text v0.28.0 // indirect golang.org/x/tools v0.35.0 // indirect + google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/grpc v1.75.0 // indirect diff --git a/go.sum b/go.sum index 7a8b7ff..7920f68 100644 --- a/go.sum +++ b/go.sum @@ -507,6 +507,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94= github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s= +github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg= +github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc= github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -568,6 +570,7 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= @@ -597,6 +600,8 @@ github.com/vcaesar/cedar v0.30.0 h1:9fSDpM7FTjjUdPiBUUa0MWYMRGSEcqgFXvppZcZ4d7Y= github.com/vcaesar/cedar v0.30.0/go.mod h1:lyuGvALuZZDPNXwpzv/9LyxW+8Y6faN7zauFezNsnik= github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4= github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= @@ -832,6 +837,9 @@ google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMt google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=