Files
dns-server/.trae/documents/plan_20260105_074926.md
2026-01-14 23:08:46 +08:00

196 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 平均响应时间计算错误修复方案
## 问题分析
通过对代码的分析,我发现平均响应时间计算错误的根本原因是:
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. 测试修复效果,验证平均响应时间计算是否正确