Skip to content

Commit 27320a9

Browse files
committed
refactor(consul): 使用地址+端口生成稳定的服务ID
统一实例ID生成逻辑到utils包,移除对uuid包的依赖 优化服务注销时的地址获取逻辑,支持环境变量覆盖
1 parent ea155da commit 27320a9

File tree

5 files changed

+126
-27
lines changed

5 files changed

+126
-27
lines changed

mock/s3/shared/middleware/consul/consul.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"time"
1010

1111
"github.com/gin-gonic/gin"
12-
"github.com/google/uuid"
1312
"github.com/hashicorp/consul/api"
1413
)
1514

@@ -51,10 +50,10 @@ func CreateConsulClient(address string, logger *observability.Logger) (ConsulCli
5150

5251
// RegisterService 注册服务到Consul
5352
func (c *DefaultConsulClient) RegisterService(ctx context.Context, service *models.ServiceInfo) error {
54-
// 使用UUID确保每个服务实例有唯一的ServiceID
55-
serviceUUID := uuid.New().String()
53+
// 使用地址+端口确保每个服务实例有唯一且稳定的ServiceID
54+
serviceID := fmt.Sprintf("%s-%s-%d", service.Name, service.Address, service.Port)
5655
registration := &api.AgentServiceRegistration{
57-
ID: fmt.Sprintf("%s-%s", service.Name, serviceUUID),
56+
ID: serviceID,
5857
Name: service.Name,
5958
Address: service.Address,
6059
Port: service.Port,

mock/s3/shared/middleware/error_injection/error_injection.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"mocks3/shared/observability"
99
"mocks3/shared/utils"
1010
"net/http"
11-
"os"
1211
"strconv"
1312
"sync"
1413
"time"
@@ -127,12 +126,7 @@ func NewMetricInjectorWithDefaults(mockErrorServiceURL string, serviceName strin
127126
// InjectMetricAnomaly 检查并注入指标异常
128127
func (mi *MetricInjector) InjectMetricAnomaly(ctx context.Context, metricName string, originalValue float64) float64 {
129128
// 计算实例标识,用于实例级注入与缓存
130-
instanceID := os.Getenv("INSTANCE_ID")
131-
if instanceID == "" {
132-
if h, err := os.Hostname(); err == nil && h != "" {
133-
instanceID = h
134-
}
135-
}
129+
instanceID := utils.GetInstanceID(mi.serviceName)
136130

137131
// 检查缓存(加入实例维度)
138132
cacheKey := mi.serviceName + ":" + instanceID + ":" + metricName

mock/s3/shared/observability/providers.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"context"
55
"fmt"
66
"mocks3/shared/observability/config"
7-
"os"
7+
"mocks3/shared/utils"
88

99
"go.opentelemetry.io/otel"
1010
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
@@ -183,15 +183,8 @@ func (p *Providers) Shutdown(ctx context.Context) error {
183183

184184
// createResource 创建OTEL资源
185185
func createResource(config *config.ObservabilityConfig) (*resource.Resource, error) {
186-
// Derive a stable service instance id
187-
instanceID := os.Getenv("INSTANCE_ID")
188-
if instanceID == "" {
189-
if h, err := os.Hostname(); err == nil && h != "" {
190-
instanceID = h
191-
} else {
192-
instanceID = "unknown-instance"
193-
}
194-
}
186+
// 使用统一的实例ID生成器
187+
instanceID := utils.GetInstanceID(config.ServiceName)
195188
return resource.New(context.Background(),
196189
resource.WithAttributes(
197190
semconv.ServiceName(config.ServiceName),

mock/s3/shared/server/service_bootstrap.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -431,13 +431,17 @@ func (sb *ServiceBootstrap) deregisterFromConsul() {
431431
// 生成服务ID (与注册时保持一致)
432432
var registerAddress string
433433
if sb.Config.GetHost() == "0.0.0.0" {
434-
// 如果绑定地址是0.0.0.0,使用hostname进行注销
435-
hostname, err := os.Hostname()
436-
if err != nil {
437-
sb.Logger.Error(ctx, "Failed to get hostname for Consul deregistration", observability.Error(err))
438-
return
434+
// 允许通过环境变量覆盖对外公布地址
435+
if envAddr := os.Getenv("ADVERTISE_ADDR"); envAddr != "" {
436+
registerAddress = envAddr
437+
} else {
438+
ip, err := detectAdvertiseAddr()
439+
if err != nil {
440+
sb.Logger.Error(ctx, "Failed to detect advertise address for Consul deregistration", observability.Error(err))
441+
return
442+
}
443+
registerAddress = ip
439444
}
440-
registerAddress = hostname
441445
} else {
442446
registerAddress = sb.Config.GetHost()
443447
}

mock/s3/shared/utils/instance.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package utils
2+
3+
import (
4+
"crypto/rand"
5+
"fmt"
6+
"os"
7+
"strings"
8+
"sync"
9+
)
10+
11+
var (
12+
cachedInstanceID string
13+
instanceIDMutex sync.RWMutex
14+
)
15+
16+
// GetInstanceID 获取实例ID,优先级:
17+
// 1. 环境变量 INSTANCE_ID
18+
// 2. 自动生成:{service_name}-{short_uuid}
19+
// 3. 后备方案:hostname
20+
func GetInstanceID(serviceName string) string {
21+
instanceIDMutex.RLock()
22+
if cachedInstanceID != "" {
23+
instanceIDMutex.RUnlock()
24+
return cachedInstanceID
25+
}
26+
instanceIDMutex.RUnlock()
27+
28+
instanceIDMutex.Lock()
29+
defer instanceIDMutex.Unlock()
30+
31+
// 双重检查,避免重复计算
32+
if cachedInstanceID != "" {
33+
return cachedInstanceID
34+
}
35+
36+
// 1. 优先使用环境变量
37+
if instanceID := os.Getenv("INSTANCE_ID"); instanceID != "" {
38+
cachedInstanceID = instanceID
39+
return cachedInstanceID
40+
}
41+
42+
// 2. 自动生成基于服务名的实例ID
43+
if serviceName != "" {
44+
if generatedID := generateInstanceID(serviceName); generatedID != "" {
45+
cachedInstanceID = generatedID
46+
return cachedInstanceID
47+
}
48+
}
49+
50+
// 3. 后备方案:使用hostname
51+
if hostname, err := os.Hostname(); err == nil && hostname != "" {
52+
cachedInstanceID = hostname
53+
return cachedInstanceID
54+
}
55+
56+
// 4. 最后的后备方案
57+
cachedInstanceID = "unknown-instance"
58+
return cachedInstanceID
59+
}
60+
61+
// generateInstanceID 生成格式为 {service_name}-{short_uuid} 的实例ID
62+
func generateInstanceID(serviceName string) string {
63+
// 清理服务名:移除常见后缀,转换为小写
64+
cleanServiceName := cleanServiceName(serviceName)
65+
66+
// 生成8位短UUID
67+
shortUUID := generateShortUUID()
68+
if shortUUID == "" {
69+
return ""
70+
}
71+
72+
return fmt.Sprintf("%s-%s", cleanServiceName, shortUUID)
73+
}
74+
75+
// cleanServiceName 清理服务名
76+
func cleanServiceName(serviceName string) string {
77+
name := strings.ToLower(serviceName)
78+
79+
// 移除常见后缀
80+
suffixes := []string{"-service", "_service", "service"}
81+
for _, suffix := range suffixes {
82+
if strings.HasSuffix(name, suffix) {
83+
name = strings.TrimSuffix(name, suffix)
84+
break
85+
}
86+
}
87+
88+
// 替换特殊字符为连字符
89+
name = strings.ReplaceAll(name, "_", "-")
90+
name = strings.ReplaceAll(name, " ", "-")
91+
92+
return name
93+
}
94+
95+
// generateShortUUID 生成8位短UUID
96+
func generateShortUUID() string {
97+
bytes := make([]byte, 4) // 4字节 = 8位十六进制字符
98+
if _, err := rand.Read(bytes); err != nil {
99+
return ""
100+
}
101+
return fmt.Sprintf("%x", bytes)
102+
}
103+
104+
// ResetInstanceID 重置缓存的实例ID(主要用于测试)
105+
func ResetInstanceID() {
106+
instanceIDMutex.Lock()
107+
defer instanceIDMutex.Unlock()
108+
cachedInstanceID = ""
109+
}

0 commit comments

Comments
 (0)