Skip to content

Commit ea155da

Browse files
committed
feat(指标异常注入): 增加实例级别的指标异常注入支持
1 parent cc9f91a commit ea155da

File tree

5 files changed

+76
-57
lines changed

5 files changed

+76
-57
lines changed

mock/s3/services/mock-error/internal/handler/mock_error_handler.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,26 +85,28 @@ func (h *MockErrorHandler) deleteMetricAnomaly(c *gin.Context) {
8585

8686
// checkMetricInjection 检查是否应该注入指标异常
8787
func (h *MockErrorHandler) checkMetricInjection(c *gin.Context) {
88-
ctx := c.Request.Context()
88+
ctx := c.Request.Context()
8989

90-
var request struct {
91-
Service string `json:"service" binding:"required"`
92-
MetricName string `json:"metric_name" binding:"required"`
93-
}
90+
var request struct {
91+
Service string `json:"service" binding:"required"`
92+
MetricName string `json:"metric_name" binding:"required"`
93+
Instance string `json:"instance"`
94+
}
9495

9596
if err := c.ShouldBindJSON(&request); err != nil {
9697
h.logger.Error(ctx, "Failed to bind metric injection check request", observability.Error(err))
9798
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
9899
return
99100
}
100101

101-
anomaly, shouldInject := h.errorService.ShouldInjectError(ctx, request.Service, request.MetricName)
102+
anomaly, shouldInject := h.errorService.ShouldInjectError(ctx, request.Service, request.MetricName, request.Instance)
102103

103-
response := gin.H{
104-
"should_inject": shouldInject,
105-
"service": request.Service,
106-
"metric_name": request.MetricName,
107-
}
104+
response := gin.H{
105+
"should_inject": shouldInject,
106+
"service": request.Service,
107+
"metric_name": request.MetricName,
108+
"instance": request.Instance,
109+
}
108110

109111
if shouldInject {
110112
response["anomaly"] = anomaly

mock/s3/services/mock-error/internal/service/mock_error_service.go

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -111,22 +111,26 @@ func (s *MockErrorService) DeleteRule(ctx context.Context, ruleID string) error
111111
}
112112

113113
// ShouldInjectError 判断是否应该注入指标异常
114-
func (s *MockErrorService) ShouldInjectError(ctx context.Context, service, metricName string) (map[string]any, bool) {
114+
func (s *MockErrorService) ShouldInjectError(ctx context.Context, service, metricName, instance string) (map[string]any, bool) {
115115
s.mu.RLock()
116116
defer s.mu.RUnlock()
117117

118118
s.stats.TotalRequests++
119119
s.stats.LastUpdated = time.Now()
120120

121-
for _, rule := range s.rules {
122-
if !rule.Enabled {
123-
continue
124-
}
121+
for _, rule := range s.rules {
122+
if !rule.Enabled {
123+
continue
124+
}
125125

126-
// 检查服务匹配
127-
if rule.Service != "" && rule.Service != service {
128-
continue
129-
}
126+
// 检查服务匹配
127+
if rule.Service != "" && rule.Service != service {
128+
continue
129+
}
130+
// 检查实例匹配(如果指定了实例,则必须匹配)
131+
if rule.Instance != "" && rule.Instance != instance {
132+
continue
133+
}
130134

131135
// 检查指标名称匹配
132136
if rule.MetricName != "" && rule.MetricName != metricName {
@@ -163,13 +167,14 @@ func (s *MockErrorService) ShouldInjectError(ctx context.Context, service, metri
163167
"rule_id": rule.ID,
164168
}
165169

166-
s.logger.Info(ctx, "Metric anomaly injected",
167-
observability.String("rule_id", rule.ID),
168-
observability.String("service", service),
169-
observability.String("metric_name", metricName),
170-
observability.String("anomaly_type", rule.AnomalyType),
171-
observability.Float64("target_value", rule.TargetValue),
172-
observability.Int("triggered_count", rule.Triggered))
170+
s.logger.Info(ctx, "Metric anomaly injected",
171+
observability.String("rule_id", rule.ID),
172+
observability.String("service", service),
173+
observability.String("instance", instance),
174+
observability.String("metric_name", metricName),
175+
observability.String("anomaly_type", rule.AnomalyType),
176+
observability.Float64("target_value", rule.TargetValue),
177+
observability.Int("triggered_count", rule.Triggered))
173178

174179
return anomaly, true
175180
}

mock/s3/shared/interfaces/error_injector.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type MetricAnomalyService interface {
1515
ListRules(ctx context.Context) ([]*models.MetricAnomalyRule, error)
1616

1717
// 指标异常注入核心功能
18-
ShouldInjectError(ctx context.Context, service, metricName string) (map[string]any, bool)
18+
ShouldInjectError(ctx context.Context, service, metricName, instance string) (map[string]any, bool)
1919
}
2020

2121
// MetricInjector HTTP指标异常注入器接口

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

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package error_injection
22

33
import (
4-
"context"
5-
"fmt"
6-
"mocks3/shared/client"
7-
"mocks3/shared/models"
8-
"mocks3/shared/observability"
9-
"mocks3/shared/utils"
10-
"net/http"
11-
"strconv"
12-
"sync"
13-
"time"
4+
"context"
5+
"fmt"
6+
"mocks3/shared/client"
7+
"mocks3/shared/models"
8+
"mocks3/shared/observability"
9+
"mocks3/shared/utils"
10+
"net/http"
11+
"os"
12+
"strconv"
13+
"sync"
14+
"time"
1415
)
1516

1617
// MetricInjectorConfig 指标异常注入器配置
@@ -125,8 +126,16 @@ func NewMetricInjectorWithDefaults(mockErrorServiceURL string, serviceName strin
125126

126127
// InjectMetricAnomaly 检查并注入指标异常
127128
func (mi *MetricInjector) InjectMetricAnomaly(ctx context.Context, metricName string, originalValue float64) float64 {
128-
// 检查缓存
129-
cacheKey := mi.serviceName + ":" + metricName
129+
// 计算实例标识,用于实例级注入与缓存
130+
instanceID := os.Getenv("INSTANCE_ID")
131+
if instanceID == "" {
132+
if h, err := os.Hostname(); err == nil && h != "" {
133+
instanceID = h
134+
}
135+
}
136+
137+
// 检查缓存(加入实例维度)
138+
cacheKey := mi.serviceName + ":" + instanceID + ":" + metricName
130139
mi.cacheMu.RLock()
131140
if cached, exists := mi.cache[cacheKey]; exists && time.Now().Before(cached.ExpiresAt) {
132141
mi.cacheMu.RUnlock()
@@ -138,17 +147,19 @@ func (mi *MetricInjector) InjectMetricAnomaly(ctx context.Context, metricName st
138147
mi.cacheMu.RUnlock()
139148

140149
// 查询Mock Error Service获取异常规则
141-
request := map[string]string{
142-
"service": mi.serviceName,
143-
"metric_name": metricName,
144-
}
145-
146-
var response struct {
147-
ShouldInject bool `json:"should_inject"`
148-
Service string `json:"service"`
149-
MetricName string `json:"metric_name"`
150-
Anomaly map[string]any `json:"anomaly,omitempty"`
151-
}
150+
request := map[string]string{
151+
"service": mi.serviceName,
152+
"metric_name": metricName,
153+
"instance": instanceID,
154+
}
155+
156+
var response struct {
157+
ShouldInject bool `json:"should_inject"`
158+
Service string `json:"service"`
159+
MetricName string `json:"metric_name"`
160+
Instance string `json:"instance"`
161+
Anomaly map[string]any `json:"anomaly,omitempty"`
162+
}
152163

153164
// 使用较短的超时时间避免影响正常指标收集
154165
opts := client.RequestOptions{

mock/s3/shared/models/error.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import (
66

77
// MetricAnomalyRule 指标异常注入规则
88
type MetricAnomalyRule struct {
9-
ID string `json:"id"`
10-
Name string `json:"name"`
11-
Service string `json:"service"` // 目标服务
12-
MetricName string `json:"metric_name"` // 目标指标名称
13-
AnomalyType string `json:"anomaly_type"`
14-
Enabled bool `json:"enabled"`
9+
ID string `json:"id"`
10+
Name string `json:"name"`
11+
Service string `json:"service"` // 目标服务
12+
Instance string `json:"instance,omitempty"` // 目标实例,可选
13+
MetricName string `json:"metric_name"` // 目标指标名称
14+
AnomalyType string `json:"anomaly_type"`
15+
Enabled bool `json:"enabled"`
1516

1617
// 异常参数
1718
TargetValue float64 `json:"target_value"` // 目标异常值

0 commit comments

Comments
 (0)