增加更多匹配的域名信息
This commit is contained in:
196
.trae/documents/plan_20260105_074926.md
Normal file
196
.trae/documents/plan_20260105_074926.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# 平均响应时间计算错误修复方案
|
||||
|
||||
## 问题分析
|
||||
通过对代码的分析,我发现平均响应时间计算错误的根本原因是:
|
||||
|
||||
1. **统计数据持久化问题**:当服务器重启时,`loadStatsData` 函数直接覆盖 `s.stats` 对象,导致 `TotalResponseTime` 和 `Queries` 之间的关系可能被破坏
|
||||
|
||||
2. **异常响应时间累计**:在某些情况下,`responseTime` 可能被错误计算为非常大的值,导致 `TotalResponseTime` 异常增长
|
||||
|
||||
3. **计算逻辑不健壮**:平均响应时间计算没有考虑异常情况,如 `Queries` 为 0 或 `TotalResponseTime` 溢出
|
||||
|
||||
4. **统计数据一致性问题**:并发访问时可能导致统计数据不一致
|
||||
|
||||
## 修复方案
|
||||
|
||||
### 1. 修复 `loadStatsData` 函数
|
||||
修改统计数据加载逻辑,确保 `TotalResponseTime` 和 `Queries` 之间的关系正确:
|
||||
|
||||
```go
|
||||
// 恢复统计数据
|
||||
s.statsMutex.Lock()
|
||||
if statsData.Stats != nil {
|
||||
// 只恢复有效数据,避免破坏统计关系
|
||||
s.stats.Queries += statsData.Stats.Queries
|
||||
s.stats.Blocked += statsData.Stats.Blocked
|
||||
s.stats.Allowed += statsData.Stats.Allowed
|
||||
s.stats.Errors += statsData.Stats.Errors
|
||||
s.stats.TotalResponseTime += statsData.Stats.TotalResponseTime
|
||||
s.stats.DNSSECQueries += statsData.Stats.DNSSECQueries
|
||||
s.stats.DNSSECSuccess += statsData.Stats.DNSSECSuccess
|
||||
s.stats.DNSSECFailed += statsData.Stats.DNSSECFailed
|
||||
|
||||
// 重新计算平均响应时间,确保一致性
|
||||
if s.stats.Queries > 0 {
|
||||
s.stats.AvgResponseTime = float64(s.stats.TotalResponseTime) / float64(s.stats.Queries)
|
||||
}
|
||||
|
||||
// 合并查询类型统计
|
||||
for k, v := range statsData.Stats.QueryTypes {
|
||||
s.stats.QueryTypes[k] += v
|
||||
}
|
||||
|
||||
// 合并来源IP统计
|
||||
for ip := range statsData.Stats.SourceIPs {
|
||||
s.stats.SourceIPs[ip] = true
|
||||
}
|
||||
|
||||
// 确保使用当前配置中的EnableDNSSEC值
|
||||
s.stats.DNSSECEnabled = s.config.EnableDNSSEC
|
||||
}
|
||||
s.statsMutex.Unlock()
|
||||
```
|
||||
|
||||
### 2. 修复响应时间计算逻辑
|
||||
在 `handleDNSRequest` 函数中,添加响应时间合理性检查:
|
||||
|
||||
```go
|
||||
// 使用上游服务器的实际响应时间(转换为毫秒)
|
||||
responseTime := int64(rtt.Milliseconds())
|
||||
// 如果rtt为0(查询失败),则使用本地计算的时间
|
||||
if responseTime == 0 {
|
||||
responseTime = time.Since(startTime).Milliseconds()
|
||||
}
|
||||
|
||||
// 添加合理性检查,避免异常大的响应时间影响统计
|
||||
if responseTime > 60000 { // 超过60秒的响应时间视为异常
|
||||
responseTime = 60000
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 优化平均响应时间计算
|
||||
修改 `updateStats` 函数,确保平均响应时间计算的健壮性:
|
||||
|
||||
```go
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.TotalResponseTime += responseTime
|
||||
// 添加防御性编程,确保Queries大于0
|
||||
if stats.Queries > 0 {
|
||||
// 使用更精确的计算方式,避免浮点数精度问题
|
||||
stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
|
||||
// 限制平均响应时间的范围,避免显示异常大的值
|
||||
if stats.AvgResponseTime > 60000 {
|
||||
stats.AvgResponseTime = 60000
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 4. 添加统计数据重置机制
|
||||
添加定期重置统计数据的功能,避免 `TotalResponseTime` 无限增长:
|
||||
|
||||
```go
|
||||
// Start 启动DNS服务器
|
||||
func (s *Server) Start() error {
|
||||
// 现有代码...
|
||||
|
||||
// 启动统计数据定期重置功能(每24小时)
|
||||
go func() {
|
||||
ticker := time.NewTicker(24 * time.Hour)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
s.resetStats()
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// 现有代码...
|
||||
}
|
||||
|
||||
// resetStats 重置统计数据
|
||||
func (s *Server) resetStats() {
|
||||
s.statsMutex.Lock()
|
||||
defer s.statsMutex.Unlock()
|
||||
|
||||
// 只重置累计值,保留配置相关值
|
||||
s.stats.TotalResponseTime = 0
|
||||
s.stats.AvgResponseTime = 0
|
||||
s.stats.Queries = 0
|
||||
s.stats.Blocked = 0
|
||||
s.stats.Allowed = 0
|
||||
s.stats.Errors = 0
|
||||
s.stats.DNSSECQueries = 0
|
||||
s.stats.DNSSECSuccess = 0
|
||||
s.stats.DNSSECFailed = 0
|
||||
s.stats.QueryTypes = make(map[string]int64)
|
||||
s.stats.SourceIPs = make(map[string]bool)
|
||||
|
||||
logger.Info("统计数据已重置")
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 修复 GetStats 函数
|
||||
确保 `GetStats` 函数正确复制所有统计数据:
|
||||
|
||||
```go// GetStats 获取DNS服务器统计信息
|
||||
func (s *Server) GetStats() *Stats {
|
||||
s.statsMutex.Lock()
|
||||
defer s.statsMutex.Unlock()
|
||||
|
||||
// 创建完整的统计数据副本
|
||||
statsCopy := &Stats{
|
||||
Queries: s.stats.Queries,
|
||||
Blocked: s.stats.Blocked,
|
||||
Allowed: s.stats.Allowed,
|
||||
Errors: s.stats.Errors,
|
||||
LastQuery: s.stats.LastQuery,
|
||||
AvgResponseTime: s.stats.AvgResponseTime,
|
||||
TotalResponseTime: s.stats.TotalResponseTime,
|
||||
QueryTypes: make(map[string]int64),
|
||||
SourceIPs: make(map[string]bool),
|
||||
CpuUsage: s.stats.CpuUsage,
|
||||
DNSSECQueries: s.stats.DNSSECQueries,
|
||||
DNSSECSuccess: s.stats.DNSSECSuccess,
|
||||
DNSSECFailed: s.stats.DNSSECFailed,
|
||||
DNSSECEnabled: s.stats.DNSSECEnabled,
|
||||
}
|
||||
|
||||
// 复制查询类型统计
|
||||
for k, v := range s.stats.QueryTypes {
|
||||
statsCopy.QueryTypes[k] = v
|
||||
}
|
||||
|
||||
// 复制来源IP统计
|
||||
for ip := range s.stats.SourceIPs {
|
||||
statsCopy.SourceIPs[ip] = true
|
||||
}
|
||||
|
||||
return statsCopy
|
||||
}
|
||||
```
|
||||
|
||||
## 修复效果
|
||||
|
||||
1. **数据一致性**:修复后,`TotalResponseTime` 和 `Queries` 之间的关系将保持正确,避免因服务器重启导致的统计数据不一致
|
||||
|
||||
2. **异常值处理**:添加响应时间合理性检查,避免异常大的响应时间影响平均响应时间计算
|
||||
|
||||
3. **计算健壮性**:优化平均响应时间计算逻辑,添加防御性编程,确保计算结果合理
|
||||
|
||||
4. **统计数据管理**:添加定期重置统计数据的功能,避免 `TotalResponseTime` 无限增长导致的溢出问题
|
||||
|
||||
5. **并发安全**:确保所有统计数据操作都是线程安全的,避免并发访问导致的数据不一致
|
||||
|
||||
## 实现步骤
|
||||
|
||||
1. 修改 `loadStatsData` 函数,修复统计数据加载逻辑
|
||||
2. 修改 `handleDNSRequest` 函数,添加响应时间合理性检查
|
||||
3. 修改 `updateStats` 函数,优化平均响应时间计算
|
||||
4. 添加 `resetStats` 函数,实现统计数据重置功能
|
||||
5. 修改 `Start` 函数,启动定期重置统计数据的协程
|
||||
6. 修复 `GetStats` 函数,确保正确复制所有统计数据
|
||||
7. 测试修复效果,验证平均响应时间计算是否正确
|
||||
Reference in New Issue
Block a user