update
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
# DNS 查询超时问题分析与修复计划
|
||||
|
||||
## 问题现状
|
||||
- 客户端请求 DNS 解析需要两次才能成功
|
||||
- 第一次返回 SERVFAIL,第二次才有正确结果
|
||||
- 错误数量高达 29,594 条
|
||||
|
||||
## 已尝试的修复
|
||||
- 将 `queryTimeout` 从 5ms 修改为 5000ms
|
||||
- 服务器已重启
|
||||
|
||||
## 需要深入调查的问题
|
||||
|
||||
### 1. 超时配置的实际生效情况
|
||||
- 检查配置文件中 queryTimeout 的单位(毫秒还是秒)
|
||||
- 验证代码中如何解析和使用这个配置值
|
||||
- 确认配置是否真正被重新加载
|
||||
|
||||
### 2. 上游 DNS 服务器可达性
|
||||
- 测试上游 DNS 服务器 (10.35.10.200, 106.14.121.141) 是否可达
|
||||
- 检查上游服务器的响应时间
|
||||
- 验证上游服务器是否支持递归查询
|
||||
|
||||
### 3. 网络层面问题
|
||||
- 检查是否有防火墙规则阻止 DNS 查询
|
||||
- 验证 UDP 53 端口是否畅通
|
||||
- 检查是否存在网络延迟或丢包
|
||||
|
||||
### 4. DNS 缓存逻辑问题
|
||||
- 检查缓存是否正常工作
|
||||
- 验证缓存 TTL 设置是否合理
|
||||
- 确认缓存命中时是否还会超时
|
||||
|
||||
### 5. 代码逻辑问题
|
||||
- 检查并行查询模式的实现
|
||||
- 验证超时处理逻辑是否正确
|
||||
- 查看是否有竞态条件导致的问题
|
||||
|
||||
## 调查步骤
|
||||
|
||||
### 第一步:检查当前配置和日志
|
||||
1. 查看当前配置文件中的 queryTimeout 值
|
||||
2. 检查最新日志中的超时设置
|
||||
3. 统计错误日志中的错误类型分布
|
||||
|
||||
### 第二步:测试上游 DNS 服务器
|
||||
1. 直接测试上游 DNS 服务器的响应
|
||||
2. 测量到上游服务器的网络延迟
|
||||
3. 验证上游服务器是否工作正常
|
||||
|
||||
### 第三步:分析代码中的超时处理
|
||||
1. 查看 forwardDNSRequestWithCache 函数中的超时逻辑
|
||||
2. 检查 dns.Client 的 Timeout 设置
|
||||
3. 验证并行查询的超时处理
|
||||
|
||||
### 第四步:检查网络配置
|
||||
1. 检查防火墙规则
|
||||
2. 测试 DNS 端口连通性
|
||||
3. 检查路由表和网络接口
|
||||
|
||||
## 可能的修复方案
|
||||
|
||||
### 方案 A:修复配置单位问题
|
||||
如果配置单位是秒而不是毫秒,需要将 5000 改为 5
|
||||
|
||||
### 方案 B:修复上游服务器配置
|
||||
如果上游服务器不可达,需要更换可用的 DNS 服务器
|
||||
|
||||
### 方案 C:修复代码中的超时逻辑
|
||||
如果代码中存在超时处理 bug,需要修改相关代码
|
||||
|
||||
### 方案 D:优化查询模式
|
||||
如果并行查询模式有问题,可以切换到 fastest-ip 模式
|
||||
|
||||
## 验证标准
|
||||
1. DNS 查询一次成功,不需要重试
|
||||
2. 错误率降低到接近 0
|
||||
3. 平均响应时间合理(<100ms)
|
||||
4. 日志中不再有大量 SERVFAIL 记录
|
||||
@@ -0,0 +1,368 @@
|
||||
# DNS 查询 SERVFAIL 问题分析(已禁用 DNSSEC)
|
||||
|
||||
## 问题描述
|
||||
|
||||
客户端请求 DNS 解析时,需要请求两次才能得到正常结果:
|
||||
|
||||
* 第一次请求:返回 SERVFAIL(服务器失败)
|
||||
|
||||
* 第二次请求:返回正常解析结果
|
||||
|
||||
## 重要信息
|
||||
|
||||
**DNSSEC 已禁用** - 因此问题与 DNSSEC 验证无关
|
||||
|
||||
## 可能的原因分析
|
||||
|
||||
### 1. 缓存初始化时机问题
|
||||
|
||||
**现象**:第一次查询时缓存为空,第二次查询命中缓存
|
||||
|
||||
**可能原因**:
|
||||
|
||||
* 缓存加载逻辑可能存在问题
|
||||
|
||||
* 第一次查询的响应没有正确缓存
|
||||
|
||||
* 缓存键生成或查找逻辑有问题
|
||||
|
||||
**相关代码位置**:
|
||||
|
||||
* `/root/dns-server/dns/cache.go` - `Get` 和 `Set` 方法
|
||||
|
||||
* `/root/dns-server/dns/server.go` - `handleCacheResponse` 方法
|
||||
|
||||
### 2. DNSSEC 验证超时或失败
|
||||
|
||||
**现象**:第一次查询触发 DNSSEC 验证,验证超时导致 SERVFAIL;第二次直接使用缓存结果
|
||||
|
||||
**可能原因**:
|
||||
|
||||
* DNSSEC 验证逻辑可能存在超时
|
||||
|
||||
* 验证失败后没有正确处理响应
|
||||
|
||||
* `verifyDNSSEC` 方法可能阻塞或失败
|
||||
|
||||
**相关代码位置**:
|
||||
|
||||
* `/root/dns-server/dns/server.go` - `verifyDNSSEC` 方法
|
||||
|
||||
* 第 1563 行左右的 DNSSEC 验证逻辑
|
||||
|
||||
### 3. 上游服务器选择逻辑问题
|
||||
|
||||
**现象**:第一次查询时服务器状态统计为空,导致选择了不可用的服务器
|
||||
|
||||
**可能原因**:
|
||||
|
||||
* 服务器健康检查逻辑可能有问题
|
||||
|
||||
* 第一次查询时没有正确选择最快的服务器
|
||||
|
||||
* 并行查询逻辑可能存在竞态条件
|
||||
|
||||
**相关代码位置**:
|
||||
|
||||
* `/root/dns-server/dns/server.go` - `forwardDNSRequestWithCache` 方法
|
||||
|
||||
* 第 1473-1600 行的并行查询逻辑
|
||||
|
||||
### 4. 响应处理逻辑问题
|
||||
|
||||
**现象**:第一次查询返回的响应被错误地判断为失败
|
||||
|
||||
**可能原因**:
|
||||
|
||||
* 响应代码判断逻辑可能有误
|
||||
|
||||
* `hasValidRecords` 判断可能过于严格
|
||||
|
||||
* 某些类型的响应被错误地拒绝
|
||||
|
||||
**相关代码位置**:
|
||||
|
||||
* `/root/dns-server/dns/server.go` - 第 871-891 行的响应验证逻辑
|
||||
|
||||
### 5. 并发锁竞争问题
|
||||
|
||||
**现象**:第一次查询时遇到锁竞争,导致超时返回 SERVFAIL
|
||||
|
||||
**可能原因**:
|
||||
|
||||
* 缓存访问锁可能与其他操作冲突
|
||||
|
||||
* 统计信息更新可能导致阻塞
|
||||
|
||||
* 日志写入可能阻塞查询流程
|
||||
|
||||
**相关代码位置**:
|
||||
|
||||
* `/root/dns-server/dns/cache.go` - 各种锁操作
|
||||
|
||||
* `/root/dns-server/dns/server.go` - 统计更新操作
|
||||
|
||||
## 调试建议
|
||||
|
||||
### 方案 1:增强日志记录
|
||||
|
||||
在关键位置添加详细日志:
|
||||
|
||||
* 缓存命中/未命中时
|
||||
|
||||
* DNSSEC 验证开始/结束/结果
|
||||
|
||||
* 上游服务器选择和响应
|
||||
|
||||
* 响应验证过程
|
||||
|
||||
### 方案 2:检查时间戳
|
||||
|
||||
分析查询日志中的时间戳:
|
||||
|
||||
* 第一次查询的实际处理时间
|
||||
|
||||
* 是否有超时现象
|
||||
|
||||
* DNSSEC 验证耗时
|
||||
|
||||
### 方案 3:复现测试
|
||||
|
||||
使用 `dig` 命令测试:
|
||||
|
||||
```bash
|
||||
# 清除缓存后测试
|
||||
dig @127.0.0.1 example.com +norecurse
|
||||
dig @127.0.0.1 example.com
|
||||
```
|
||||
|
||||
## 根本原因已确定
|
||||
|
||||
经过详细代码分析,发现问题出在 **并行请求模式下的响应处理逻辑缺陷**:
|
||||
|
||||
### 问题流程:
|
||||
|
||||
1. **第一次查询**:
|
||||
|
||||
* 缓存未命中,向上游服务器发起并行查询
|
||||
|
||||
* 代码进入并行请求模式(第 1490-1777 行)
|
||||
|
||||
* 收到上游返回的响应,`resp.response.Rcode == dns.RcodeSuccess`
|
||||
|
||||
* **关键问题**:在 `noDNSSEC` 模式下(第 1572-1588 行),代码直接返回第一个成功响应
|
||||
|
||||
* **但是**:没有检查响应是否包含有效记录(Answer/Ns/Extra)
|
||||
|
||||
* 如果上游返回的是 `Rcode=Success` 但 `Answer 为空` 的响应(例如某些 DNS 服务器的截断响应)
|
||||
|
||||
* 该响应被直接发送给客户端
|
||||
|
||||
* 在 `handleUpstreamRequest` 第 870-891 行,由于 `hasValidRecords` 检查失败(Answer 为空),`response.Rcode` 没有被设置为 `RcodeSuccess`
|
||||
|
||||
* 最终客户端收到 SERVFAIL 错误
|
||||
|
||||
2. **第二次查询**:
|
||||
|
||||
* 第一次查询的响应已经被缓存(即使 Answer 为空,只要 Rcode=Success 就会被缓存)
|
||||
|
||||
* 直接从缓存返回
|
||||
|
||||
* 但此时缓存的可能是完整响应(因为上游第二次返回了完整结果)
|
||||
|
||||
* 客户端收到正常解析结果
|
||||
|
||||
### 具体代码问题:
|
||||
|
||||
**问题 1:并行模式下缺少 hasValidRecords 检查**
|
||||
|
||||
在 `/root/dns-server/dns/server.go` 第 1572-1588 行(并行模式,noDNSSEC 分支):
|
||||
|
||||
```go
|
||||
if noDNSSEC && !bestResponseSent {
|
||||
// 不验证 DNSSEC 的域名:直接返回第一个成功响应
|
||||
if fastestResponse == nil || resp.rtt < fastestRtt {
|
||||
fastestResponse = resp.response
|
||||
fastestRtt = resp.rtt
|
||||
fastestServer = resp.server
|
||||
fastestDnssecServer = dnssecServerForResponse
|
||||
fastestHasDnssec = false
|
||||
|
||||
// 立即发送结果,快速返回(只发送一次)
|
||||
resultChan <- struct {
|
||||
response: fastestResponse, // 可能 Answer 为空!
|
||||
...
|
||||
}
|
||||
bestResponseSent = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**对比**:在串行模式(第 1452-1469 行)中有 `hasValidRecords` 检查:
|
||||
|
||||
```go
|
||||
// 检查响应是否包含有效的记录,如果包含,将 Rcode 设置为成功
|
||||
hasValidRecords := false
|
||||
if len(response.Answer) > 0 {
|
||||
hasValidRecords = true
|
||||
} else if len(response.Ns) > 0 {
|
||||
hasValidRecords = true
|
||||
} else if len(response.Extra) > 0 {
|
||||
for _, rr := range response.Extra {
|
||||
if rr.Header().Rrtype != dns.TypeOPT {
|
||||
hasValidRecords = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hasValidRecords {
|
||||
response.Rcode = dns.RcodeSuccess
|
||||
}
|
||||
```
|
||||
|
||||
**问题 2:Rcode 判断逻辑不准确**
|
||||
|
||||
第 1562 行只检查 `resp.response.Rcode == dns.RcodeSuccess`,但有些 DNS 服务器可能返回:
|
||||
|
||||
* `Rcode=Success` 但 `Answer 为空`(例如需要 TCP 回退时)
|
||||
|
||||
* 这种情况下应该继续等待其他服务器的响应,而不是立即返回
|
||||
|
||||
## 建议的修复步骤
|
||||
|
||||
### 方案 1:在并行模式下添加 hasValidRecords 检查(推荐)
|
||||
|
||||
**问题**:并行模式(noDNSSEC 分支)缺少 hasValidRecords 检查
|
||||
|
||||
**解决方案**:
|
||||
在并行模式的快速返回逻辑中添加 hasValidRecords 检查,确保只返回包含有效记录的响应
|
||||
|
||||
```go
|
||||
// 修改前
|
||||
if noDNSSEC && !bestResponseSent {
|
||||
// 不验证 DNSSEC 的域名:直接返回第一个成功响应
|
||||
if fastestResponse == nil || resp.rtt < fastestRtt {
|
||||
fastestResponse = resp.response
|
||||
fastestRtt = resp.rtt
|
||||
fastestServer = resp.server
|
||||
fastestDnssecServer = dnssecServerForResponse
|
||||
fastestHasDnssec = false
|
||||
|
||||
// 立即发送结果,快速返回(只发送一次)
|
||||
resultChan <- struct {
|
||||
response: fastestResponse,
|
||||
...
|
||||
}
|
||||
bestResponseSent = true
|
||||
}
|
||||
}
|
||||
|
||||
// 修改后
|
||||
if noDNSSEC && !bestResponseSent {
|
||||
// 不验证 DNSSEC 的域名:返回第一个包含有效记录的成功响应
|
||||
if fastestResponse == nil || resp.rtt < fastestRtt {
|
||||
// 检查响应是否包含有效记录
|
||||
hasValidRecords := false
|
||||
if len(resp.response.Answer) > 0 {
|
||||
hasValidRecords = true
|
||||
} else if len(resp.response.Ns) > 0 {
|
||||
hasValidRecords = true
|
||||
} else if len(resp.response.Extra) > 0 {
|
||||
for _, rr := range resp.response.Extra {
|
||||
if rr.Header().Rrtype != dns.TypeOPT {
|
||||
hasValidRecords = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 只有包含有效记录才返回
|
||||
if hasValidRecords {
|
||||
fastestResponse = resp.response
|
||||
fastestRtt = resp.rtt
|
||||
fastestServer = resp.server
|
||||
fastestDnssecServer = dnssecServerForResponse
|
||||
fastestHasDnssec = false
|
||||
|
||||
// 立即发送结果,快速返回(只发送一次)
|
||||
resultChan <- struct {
|
||||
response: fastestResponse,
|
||||
...
|
||||
}
|
||||
bestResponseSent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 方案 2:改进 Rcode 判断逻辑
|
||||
|
||||
**问题**:只检查 Rcode,不检查 Answer 内容
|
||||
|
||||
**解决方案**:
|
||||
将 Rcode 检查和 Answer 检查结合起来,确保响应真正有效
|
||||
|
||||
```go
|
||||
// 修改前
|
||||
if resp.response.Rcode == dns.RcodeSuccess || resp.response.Rcode == dns.RcodeNameError {
|
||||
// 处理响应
|
||||
}
|
||||
|
||||
// 修改后
|
||||
// 检查 Rcode 和 Answer 内容
|
||||
isValidResponse := false
|
||||
if resp.response.Rcode == dns.RcodeSuccess {
|
||||
// 成功响应需要包含有效记录
|
||||
if len(resp.response.Answer) > 0 || len(resp.response.Ns) > 0 {
|
||||
isValidResponse = true
|
||||
}
|
||||
} else if resp.response.Rcode == dns.RcodeNameError {
|
||||
// NXDOMAIN 响应可以没有 Answer
|
||||
isValidResponse = true
|
||||
}
|
||||
|
||||
if isValidResponse {
|
||||
// 处理有效响应
|
||||
}
|
||||
```
|
||||
|
||||
### 方案 3:增加重试机制
|
||||
|
||||
**问题**:第一次返回空 Answer 后没有重试
|
||||
|
||||
**解决方案**:
|
||||
检测到空 Answer 时,继续等待其他服务器的响应或重试
|
||||
|
||||
```go
|
||||
// 在并行模式中添加重试逻辑
|
||||
if len(resp.response.Answer) == 0 && resp.response.Rcode == dns.RcodeSuccess {
|
||||
// Answer 为空,可能是截断响应,继续等待其他服务器
|
||||
logger.Debug("响应 Answer 为空,继续等待其他服务器", "domain", domain)
|
||||
continue // 不立即返回,继续等待
|
||||
}
|
||||
```
|
||||
|
||||
### 方案 4:调整上游 DNS 服务器配置
|
||||
|
||||
**问题**:某些上游 DNS 服务器可能返回截断响应
|
||||
|
||||
**解决方案**:
|
||||
|
||||
1. 检查上游 DNS 服务器配置
|
||||
2. 尝试更换更可靠的上游 DNS 服务器
|
||||
3. 增加 UDP 缓冲区大小,避免截断
|
||||
|
||||
## 推荐修复顺序
|
||||
|
||||
1. **立即修复**:方案 1(在并行模式下添加 hasValidRecords 检查)- 直接解决问题
|
||||
2. **优化改进**:方案 2(改进 Rcode 判断逻辑)- 提高健壮性
|
||||
3. **可选优化**:方案 3(增加重试机制)- 进一步提高可靠性
|
||||
|
||||
## 需要查看的关键代码
|
||||
|
||||
1. `verifyDNSSEC` 方法实现
|
||||
2. `forwardDNSRequestWithCache` 中的 DNSSEC 验证逻辑
|
||||
3. `handleUpstreamRequest` 中的响应处理
|
||||
4. DNSSEC 相关配置和超时设置
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
# 域名信息管理项重新设计计划
|
||||
|
||||
## 1. 现状分析
|
||||
|
||||
### 1.1 现有实现
|
||||
|
||||
- **后端实现**:`shield/domain_info_manager.go` 实现了域名信息管理器
|
||||
- **前端实现**:`static/js/modules/domain-info.js` 实现了前端管理界面
|
||||
- **API接口**:`http/domain_info_handlers.go` 提供了相关API
|
||||
- **配置管理**:`config/config.go` 定义了配置结构
|
||||
|
||||
### 1.2 现有功能
|
||||
|
||||
1. **三个列表管理**:
|
||||
- 域名信息列表 (domain-info)
|
||||
- 威胁域名列表 (threat-database)
|
||||
- 跟踪器列表 (tracker)
|
||||
|
||||
2. **现有API**:
|
||||
- `/api/domain-info` - 获取域名信息列表和统计
|
||||
- `/api/domain-info/update` - 更新所有域名信息
|
||||
- `/api/domain-info/update/{type}` - 更新指定类型的域名信息
|
||||
- `/api/domain-info/query` - 查询单个域名信息
|
||||
|
||||
3. **现有前端功能**:
|
||||
- 显示三个列表的详细信息
|
||||
- 显示各列表的规则数量
|
||||
- 显示最后更新时间
|
||||
- 支持更新所有列表和单个列表
|
||||
|
||||
### 1.3 问题与不足
|
||||
|
||||
1. **数据更新机制**:
|
||||
- 威胁数据库的URL配置错误(使用了src路径而非raw路径)
|
||||
- 缺少更新状态的实时反馈
|
||||
|
||||
2. **前端界面**:
|
||||
- 界面设计较为简单
|
||||
- 缺少视觉层次感和交互体验
|
||||
|
||||
3. **数据管理**:
|
||||
- 缺少数据验证和错误处理
|
||||
- 缓存机制可以进一步优化
|
||||
|
||||
4. **用户功能**:
|
||||
- 缺少用户增加和删除列表的功能
|
||||
|
||||
### 1.4 列表文件分析
|
||||
|
||||
通过下载和分析列表文件,了解到:
|
||||
|
||||
1. **domain-info.json**:
|
||||
- 格式:JSON
|
||||
- 结构:
|
||||
- categories: 分类信息(键值对)
|
||||
- domains: 域名信息(嵌套结构,第一层公司名称,第二层具体域名服务)
|
||||
- timeUpdated: 更新时间
|
||||
- 规模:4444行
|
||||
- 用途:提供域名分类信息
|
||||
|
||||
2. **threats-database.csv**:
|
||||
- 格式:CSV
|
||||
- 结构:包含 type, name, riskLevel, domain 字段
|
||||
- 规模:73012行(约3.1MB)
|
||||
- 用途:提供威胁域名数据库
|
||||
|
||||
3. **trackers.json**:
|
||||
- 格式:JSON
|
||||
- 结构:
|
||||
- categories: 分类信息
|
||||
- timeUpdated: 更新时间
|
||||
- trackerDomains: 跟踪器域名
|
||||
- trackers: 跟踪器信息(嵌套结构)
|
||||
- 规模:25345行
|
||||
- 用途:提供跟踪器域名信息
|
||||
|
||||
### 1.5 嵌套结构处理注意事项
|
||||
|
||||
- **domain-info.json**:domains字段包含多层嵌套结构,需要递归处理
|
||||
- **trackers.json**:trackers字段包含嵌套结构,需要正确解析
|
||||
- **数据量级**:威胁数据库较大(7万多行),需要考虑性能优化
|
||||
|
||||
## 2. 重新设计方案
|
||||
|
||||
### 2.1 后端改进
|
||||
|
||||
1. **修复URL配置**:
|
||||
- 修正威胁数据库的URL,使用raw路径
|
||||
|
||||
2. **增强更新机制**:
|
||||
- 添加更新状态的实时反馈
|
||||
- 改进错误处理和日志记录
|
||||
|
||||
3. **优化缓存策略**:
|
||||
- 实现更智能的缓存过期机制
|
||||
- 增加缓存验证
|
||||
|
||||
4. **添加API端点**:
|
||||
- `/api/domain-info/status` - 获取更新状态
|
||||
- `/api/domain-info/refresh` - 强制刷新缓存
|
||||
- `/api/domain-info/add` - 添加新的域名信息列表
|
||||
- `/api/domain-info/remove` - 删除域名信息列表
|
||||
|
||||
5. **实现用户增删功能**:
|
||||
- 支持用户添加自定义域名信息列表
|
||||
- 支持用户删除现有列表
|
||||
- 实现列表配置的持久化存储
|
||||
|
||||
6. **嵌套结构处理**:
|
||||
- 优化JSON嵌套结构的解析和处理
|
||||
- 实现高效的嵌套数据存储和查询
|
||||
|
||||
### 2.2 前端改进
|
||||
|
||||
1. **界面重新设计**:
|
||||
- 采用卡片式布局,增强视觉层次感
|
||||
- 添加统计信息概览区域
|
||||
- 优化表格布局和交互体验
|
||||
|
||||
2. **功能增强**:
|
||||
- 添加更新进度指示
|
||||
- 实现批量操作功能
|
||||
- 增加数据导出功能
|
||||
- 添加列表增加和删除功能
|
||||
- 实现列表编辑功能
|
||||
|
||||
3. **响应式设计**:
|
||||
- 优化移动端显示
|
||||
- 确保在不同设备上的良好体验
|
||||
|
||||
4. **数据展示优化**:
|
||||
- 实现嵌套结构的可视化展示
|
||||
- 支持展开/折叠嵌套数据
|
||||
|
||||
### 2.3 数据管理优化
|
||||
|
||||
1. **数据验证**:
|
||||
- 增加URL格式验证
|
||||
- 实现数据完整性检查
|
||||
- 验证列表类型和格式
|
||||
|
||||
2. **错误处理**:
|
||||
- 提供更详细的错误信息
|
||||
- 实现优雅的错误展示
|
||||
|
||||
3. **性能优化**:
|
||||
- 减少不必要的网络请求
|
||||
- 优化数据加载和渲染
|
||||
- 实现大型列表的分页加载
|
||||
- 优化嵌套结构的处理性能
|
||||
|
||||
## 3. 具体实现步骤
|
||||
|
||||
### 3.1 后端修改
|
||||
|
||||
1. **修复配置**:
|
||||
- 修正 `config/config.go` 中的威胁数据库URL
|
||||
|
||||
2. **增强域名信息管理器**:
|
||||
- 在 `shield/domain_info_manager.go` 中添加更新状态管理
|
||||
- 实现更详细的错误处理
|
||||
- 完善列表增删功能
|
||||
- 优化嵌套JSON结构的解析
|
||||
|
||||
3. **扩展API接口**:
|
||||
- 在 `http/domain_info_handlers.go` 中添加新的API端点
|
||||
- 实现状态查询、缓存刷新、列表增删功能
|
||||
|
||||
4. **实现配置持久化**:
|
||||
- 确保用户添加的列表配置能够持久保存
|
||||
- 实现配置文件的自动更新
|
||||
|
||||
### 3.2 前端修改
|
||||
|
||||
1. **重新设计界面**:
|
||||
- 更新 `static/js/modules/domain-info.js` 中的渲染逻辑
|
||||
- 实现卡片式布局和统计概览
|
||||
|
||||
2. **增强交互功能**:
|
||||
- 添加更新进度指示
|
||||
- 实现批量操作和数据导出
|
||||
- 添加列表增加和删除功能
|
||||
- 实现列表编辑表单
|
||||
|
||||
3. **优化用户体验**:
|
||||
- 添加加载动画和过渡效果
|
||||
- 实现更友好的错误提示
|
||||
- 提供操作确认机制
|
||||
|
||||
4. **实现嵌套数据展示**:
|
||||
- 支持展开/折叠嵌套结构
|
||||
- 优化大型嵌套数据的渲染性能
|
||||
|
||||
### 3.3 测试与验证
|
||||
|
||||
1. **功能测试**:
|
||||
- 测试所有API端点
|
||||
- 验证数据更新功能
|
||||
- 测试列表增删功能
|
||||
- 测试错误处理
|
||||
|
||||
2. **性能测试**:
|
||||
- 测试数据加载速度
|
||||
- 验证缓存机制
|
||||
- 测试响应式布局
|
||||
|
||||
3. **安全性测试**:
|
||||
- 验证输入验证
|
||||
- 测试错误处理安全性
|
||||
|
||||
## 4. 预期效果
|
||||
|
||||
### 4.1 功能改进
|
||||
|
||||
- ✅ 正确显示三个列表的数量
|
||||
- ✅ 支持更新所有/单个列表
|
||||
- ✅ 显示更新时间
|
||||
- ✅ 提供更新状态反馈
|
||||
- ✅ 实现批量操作功能
|
||||
- ✅ 支持用户增加和删除列表
|
||||
- ✅ 实现列表编辑功能
|
||||
- ✅ 正确处理JSON嵌套结构
|
||||
|
||||
### 4.2 界面改进
|
||||
|
||||
- ✅ 现代化的卡片式布局
|
||||
- ✅ 清晰的统计信息概览
|
||||
- ✅ 良好的响应式设计
|
||||
- ✅ 流畅的交互体验
|
||||
- ✅ 直观的列表管理界面
|
||||
- ✅ 嵌套数据的可视化展示
|
||||
|
||||
### 4.3 性能改进
|
||||
|
||||
- ✅ 优化的数据加载速度
|
||||
- ✅ 智能的缓存机制
|
||||
- ✅ 减少网络请求
|
||||
- ✅ 提高系统稳定性
|
||||
- ✅ 支持大型列表的高效处理
|
||||
- ✅ 优化嵌套结构的处理性能
|
||||
|
||||
## 5. 风险与应对措施
|
||||
|
||||
### 5.1 风险
|
||||
|
||||
1. **数据加载失败**:
|
||||
- 远程数据源不可用
|
||||
- 网络连接问题
|
||||
|
||||
2. **性能问题**:
|
||||
- 大量数据导致加载缓慢
|
||||
- 缓存策略不当
|
||||
- 嵌套结构处理性能问题
|
||||
|
||||
3. **兼容性问题**:
|
||||
- 浏览器兼容性
|
||||
- 移动设备适配
|
||||
|
||||
4. **用户操作风险**:
|
||||
- 误删除重要列表
|
||||
- 添加无效的列表配置
|
||||
|
||||
5. **数据结构风险**:
|
||||
- JSON嵌套结构解析错误
|
||||
- 数据格式不一致
|
||||
|
||||
### 5.2 应对措施
|
||||
|
||||
1. **数据加载失败**:
|
||||
- 实现错误重试机制
|
||||
- 使用缓存数据作为备用
|
||||
- 提供详细的错误信息
|
||||
|
||||
2. **性能问题**:
|
||||
- 实现数据分页加载
|
||||
- 优化缓存策略
|
||||
- 使用异步加载
|
||||
- 实现嵌套结构的懒加载
|
||||
|
||||
3. **兼容性问题**:
|
||||
- 使用现代前端技术
|
||||
- 测试主流浏览器
|
||||
- 实现响应式设计
|
||||
|
||||
4. **用户操作风险**:
|
||||
- 实现操作确认机制
|
||||
- 提供默认列表恢复功能
|
||||
- 验证用户输入的有效性
|
||||
|
||||
5. **数据结构风险**:
|
||||
- 实现健壮的JSON解析
|
||||
- 添加数据结构验证
|
||||
- 提供详细的错误提示
|
||||
|
||||
## 6. 实施步骤
|
||||
|
||||
1. **准备阶段**:
|
||||
- 代码分析和问题识别
|
||||
- 设计新的界面和功能
|
||||
|
||||
2. **后端开发**:
|
||||
- 修复URL配置
|
||||
- 扩展API接口
|
||||
- 实现列表增删功能
|
||||
- 优化嵌套结构处理
|
||||
|
||||
3. **前端开发**:
|
||||
- 重新设计界面
|
||||
- 实现交互功能
|
||||
- 添加列表管理功能
|
||||
- 实现嵌套数据展示
|
||||
|
||||
4. **测试阶段**:
|
||||
- 功能测试
|
||||
- 性能测试
|
||||
- 兼容性测试
|
||||
|
||||
5. **部署阶段**:
|
||||
- 代码审查
|
||||
- 部署更新
|
||||
- 监控运行状态
|
||||
|
||||
## 7. 结论
|
||||
|
||||
通过重新设计域名信息管理项,我们将实现:
|
||||
|
||||
1. **功能完善**:确保三个列表的数量显示、更新功能、时间显示,以及用户增删列表的能力,同时正确处理JSON嵌套结构
|
||||
2. **界面优化**:提供现代化、美观的用户界面,支持嵌套数据的可视化展示
|
||||
3. **性能提升**:优化数据加载和缓存机制,提高嵌套结构处理性能
|
||||
4. **用户体验**:提供流畅、直观的操作体验
|
||||
|
||||
这些改进将使域名信息管理系统更加实用、可靠和用户友好,满足用户对域名信息管理的各种需求。
|
||||
@@ -0,0 +1,186 @@
|
||||
# 域名信息远程列表功能实施计划
|
||||
|
||||
## 概述
|
||||
在 DNS 服务器的屏蔽管理系统中增加三类域名信息的远程异步加载功能,用于增强日志详情 API 和威胁告警 API 的域名信息查询能力。
|
||||
|
||||
## 需要添加的远程域名信息
|
||||
|
||||
### 1. 域名信息列表
|
||||
- **URL**: `https://gitea.amazehome.xyz/AMAZEHOME/domain-info/raw/branch/main/domains/domain-info.json`
|
||||
- **用途**: 用于查询日志详情 API 中的域名信息
|
||||
- **格式**: JSON
|
||||
|
||||
### 2. 威胁告警域名列表
|
||||
- **URL**: `https://gitea.amazehome.xyz/AMAZEHOME/domain-info/src/branch/main/threats/threats-database.csv`
|
||||
- **用途**: 用于威胁告警 API 的威胁域名信息
|
||||
- **格式**: CSV
|
||||
|
||||
### 3. 跟踪器域名列表
|
||||
- **URLs**:
|
||||
- `https://gitea.amazehome.xyz/AMAZEHOME/domain-info/raw/branch/main/tracker/trackers.json`
|
||||
- **用途**: 用于查询日志详情 API 中的跟踪器信息
|
||||
- **格式**: JSON
|
||||
|
||||
## 实施步骤
|
||||
|
||||
### 步骤 1: 扩展配置文件结构
|
||||
1.1 在 `config/config.go` 中添加新的配置结构:
|
||||
- `DomainInfoConfig` - 域名信息配置
|
||||
- 包含三个远程列表的配置条目(类似 `BlacklistEntry`)
|
||||
- 添加更新间隔、启用状态等配置项
|
||||
|
||||
1.2 在 `Config` 结构体中添加 `DomainInfo DomainInfoConfig` 字段
|
||||
|
||||
1.3 实现配置解析函数:
|
||||
- `parseDomainInfoLists()` - 解析域名信息列表配置
|
||||
- 支持 INI 格式配置
|
||||
|
||||
### 步骤 2: 创建域名信息管理器
|
||||
2.1 创建新文件 `shield/domain_info_manager.go`
|
||||
|
||||
2.2 实现 `DomainInfoManager` 结构体:
|
||||
- 存储域名信息数据(JSON 格式)
|
||||
- 存储威胁域名数据(CSV 格式)
|
||||
- 存储跟踪器数据(JSON 格式)
|
||||
- 缓存管理和过期检查机制
|
||||
- 异步加载和更新逻辑
|
||||
|
||||
2.3 实现核心方法:
|
||||
- `LoadDomainInfo()` - 加载所有域名信息
|
||||
- `fetchRemoteDomainInfo()` - 获取远程域名信息
|
||||
- `fetchThreatDatabase()` - 获取威胁数据库
|
||||
- `fetchTrackerInfo()` - 获取跟踪器信息
|
||||
- `shouldUpdateCache()` - 检查缓存是否需要更新
|
||||
- `GetDomainInfo(domain)` - 查询域名信息
|
||||
- `GetThreatInfo(domain)` - 查询威胁信息
|
||||
- `GetTrackerInfo(domain)` - 查询跟踪器信息
|
||||
- `GetAllDomainInfo()` - 获取所有域名信息(用于 API)
|
||||
|
||||
2.4 实现缓存机制:
|
||||
- 使用内存缓存存储加载的数据
|
||||
- 定期后台刷新(根据配置的更新间隔)
|
||||
- 支持手动刷新
|
||||
|
||||
### 步骤 3: 实现后端 API
|
||||
3.1 在 `http/server.go` 中添加新的 API 路由:
|
||||
- `/api/shield/domain-info` - 获取域名信息列表(GET)
|
||||
- `/api/shield/domain-info/update` - 手动更新所有域名信息(POST)
|
||||
- `/api/shield/domain-info/{type}/update` - 更新指定类型的域名信息(POST)
|
||||
- `/api/domain/info` - 查询单个域名的详细信息(GET)
|
||||
|
||||
3.2 实现 API 处理函数:
|
||||
- `handleDomainInfo()` - 处理域名信息列表的获取
|
||||
- `handleUpdateDomainInfo()` - 处理手动更新请求
|
||||
- `handleDomainInfoQuery()` - 处理域名信息查询
|
||||
|
||||
3.3 在服务器初始化时:
|
||||
- 创建 `DomainInfoManager` 实例
|
||||
- 启动后台定期更新任务
|
||||
|
||||
### 步骤 4: 实现前端界面
|
||||
4.1 创建 `static/js/modules/domain-info.js` 模块:
|
||||
- `loadDomainInfoLists()` - 加载域名信息列表
|
||||
- `updateDomainInfo(type)` - 更新域名信息
|
||||
- `renderDomainInfoLists()` - 渲染域名信息列表表格
|
||||
- `showDomainInfoDetails()` - 显示域名信息详情
|
||||
|
||||
4.2 在 `static/js/shield.js` 中添加:
|
||||
- `loadRemoteDomainInfoLists()` - 异步加载远程域名信息列表
|
||||
- `updateDomainInfoListsTable()` - 更新域名信息列表 UI
|
||||
- 添加状态指示器和通知逻辑
|
||||
|
||||
4.3 创建前端管理界面 `static/domain-info.html`(或集成到现有 shield.html):
|
||||
- 域名信息列表管理表格
|
||||
- 显示各列表的状态、规则数量、最后更新时间
|
||||
- 手动更新按钮
|
||||
- 启用/禁用切换
|
||||
|
||||
### 步骤 5: 集成到日志详情 API
|
||||
5.1 修改日志详情查询接口:
|
||||
- 在返回的日志详情中添加域名信息字段
|
||||
- 添加跟踪器信息字段
|
||||
- 如果域名在威胁数据库中,添加威胁告警信息
|
||||
|
||||
5.2 优化查询性能:
|
||||
- 使用缓存避免重复查询
|
||||
- 批量查询优化
|
||||
|
||||
### 步骤 6: 集成到威胁告警 API
|
||||
6.1 修改威胁告警查询接口:
|
||||
- 使用远程威胁数据库进行匹配
|
||||
- 返回威胁类型、风险等级等详细信息
|
||||
|
||||
### 步骤 7: 测试和文档
|
||||
7.1 功能测试:
|
||||
- 测试远程列表加载
|
||||
- 测试缓存机制
|
||||
- 测试 API 接口
|
||||
- 测试前端界面
|
||||
|
||||
7.2 更新文档:
|
||||
- 更新 README.md
|
||||
- 添加配置示例
|
||||
- 更新 API 文档
|
||||
|
||||
## 技术要点
|
||||
|
||||
### 异步加载机制
|
||||
- 使用 goroutine 进行后台异步加载
|
||||
- 使用 channel 或 context 控制加载流程
|
||||
- 支持并发加载多个远程列表
|
||||
|
||||
### 缓存策略
|
||||
- 内存缓存 + 文件缓存双重机制
|
||||
- 基于时间的过期策略
|
||||
- 支持手动刷新和自动刷新
|
||||
|
||||
### 错误处理
|
||||
- 网络错误的重试机制
|
||||
- 加载失败时的降级处理(使用缓存)
|
||||
- 详细的错误日志记录
|
||||
|
||||
### 性能优化
|
||||
- 批量处理减少锁竞争
|
||||
- 使用 RWMutex 优化读写性能
|
||||
- 延迟加载和按需加载
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 新增文件
|
||||
- `shield/domain_info_manager.go` - 域名信息管理器
|
||||
- `static/js/modules/domain-info.js` - 前端域名信息管理模块
|
||||
- `static/domain-info.html` - 域名信息管理界面(可选,如集成到现有页面则不需要)
|
||||
|
||||
### 修改文件
|
||||
- `config/config.go` - 添加域名信息配置
|
||||
- `http/server.go` - 添加 API 路由和处理函数
|
||||
- `shield/manager.go` - 集成域名信息管理(可选)
|
||||
- `static/js/shield.js` - 添加前端加载逻辑
|
||||
- `README.md` - 更新文档
|
||||
|
||||
## 配置示例
|
||||
|
||||
```ini
|
||||
[domainInfo]
|
||||
; 域名信息列表配置
|
||||
; 格式:name = url,enabled
|
||||
domainInfoList = https://gitea.amazehome.xyz/AMAZEHOME/domain-info/raw/branch/main/domains/domain-info.json,true
|
||||
threatDatabase = https://gitea.amazehome.xyz/AMAZEHOME/domain-info/src/branch/main/threats/threats-database.csv,true
|
||||
trackerList = https://gitea.amazehome.xyz/AMAZEHOME/domain-info/raw/branch/main/tracker/trackers.json,true
|
||||
|
||||
; 更新间隔(秒)
|
||||
updateInterval = 3600
|
||||
|
||||
; 是否启用自动更新
|
||||
enableAutoUpdate = true
|
||||
```
|
||||
|
||||
## 时间估算
|
||||
- 步骤 1: 配置文件扩展 - 30 分钟
|
||||
- 步骤 2: 域名信息管理器 - 2 小时
|
||||
- 步骤 3: 后端 API 实现 - 1 小时
|
||||
- 步骤 4: 前端界面实现 - 1.5 小时
|
||||
- 步骤 5-6: API 集成 - 1 小时
|
||||
- 步骤 7: 测试和文档 - 1 小时
|
||||
|
||||
**总计**: 约 7-8 小时
|
||||
@@ -0,0 +1,87 @@
|
||||
# DNS服务器间歇性SERVFAIL错误修复计划
|
||||
|
||||
## 问题分析
|
||||
|
||||
通过分析代码和测试脚本,发现DNS服务器存在间歇性SERVFAIL错误,表现为:
|
||||
- 第一次查询失败(返回SERVFAIL)
|
||||
- 第二次查询成功
|
||||
|
||||
## 可能的原因
|
||||
|
||||
1. **上游服务器连接问题**:上游DNS服务器偶尔响应失败,导致第一次查询时所有服务器都失败
|
||||
2. **缓存机制问题**:缓存未正确处理失败响应,导致第二次查询时能够从缓存中获取到正确的响应
|
||||
3. **并行查询逻辑问题**:在parallel模式下,响应处理可能存在竞态条件
|
||||
4. **超时处理问题**:超时设置不合理或处理不当
|
||||
|
||||
## 代码分析
|
||||
|
||||
### 关键问题点
|
||||
|
||||
1. **forwardDNSRequestWithCache函数**(server.go:1191):
|
||||
- 当所有上游服务器都失败时,可能返回nil响应,导致SERVFAIL
|
||||
- 并行查询时的响应处理逻辑可能存在问题
|
||||
|
||||
2. **handleUpstreamRequest函数**(server.go:825):
|
||||
- 当response为nil时,直接返回SERVFAIL
|
||||
|
||||
3. **客户端池管理**:
|
||||
- 使用sync.Pool管理DNS客户端实例,但可能存在并发问题
|
||||
|
||||
## 修复方案
|
||||
|
||||
### 1. 改进上游请求处理逻辑
|
||||
|
||||
- **增加重试机制**:当第一次查询失败时,自动重试一次
|
||||
- **优化超时设置**:确保超时时间合理,避免过早超时
|
||||
- **改进错误处理**:当所有上游服务器都失败时,尝试使用备选服务器
|
||||
|
||||
### 2. 优化缓存机制
|
||||
|
||||
- **缓存失败响应**:暂时缓存失败响应,避免短时间内重复查询失败的域名
|
||||
- **改进缓存键设计**:确保缓存键能够正确标识不同的查询条件
|
||||
|
||||
### 3. 改进并行查询逻辑
|
||||
|
||||
- **优化响应处理**:确保并行查询时能够正确处理所有响应
|
||||
- **增加响应验证**:验证响应的有效性,避免返回无效响应
|
||||
|
||||
### 4. 增加监控和日志
|
||||
|
||||
- **增加详细日志**:记录每次查询的详细信息,包括上游服务器响应情况
|
||||
- **增加统计信息**:统计上游服务器的响应成功率,便于问题定位
|
||||
|
||||
## 实施步骤
|
||||
|
||||
1. **修改forwardDNSRequestWithCache函数**:
|
||||
- 增加重试机制
|
||||
- 优化错误处理逻辑
|
||||
- 改进并行查询的响应处理
|
||||
|
||||
2. **修改handleUpstreamRequest函数**:
|
||||
- 增加对nil响应的处理
|
||||
- 优化响应验证
|
||||
|
||||
3. **修改缓存机制**:
|
||||
- 增加失败响应的缓存
|
||||
- 改进缓存键设计
|
||||
|
||||
4. **增加监控和日志**:
|
||||
- 增加详细的查询日志
|
||||
- 增加上游服务器响应统计
|
||||
|
||||
5. **测试验证**:
|
||||
- 使用test_servfail.sh脚本测试修复效果
|
||||
- 进行压力测试,确保修复后系统稳定
|
||||
|
||||
## 预期效果
|
||||
|
||||
- 消除间歇性SERVFAIL错误
|
||||
- 提高DNS服务器的稳定性和可靠性
|
||||
- 减少对上游服务器的重复查询
|
||||
- 提供更详细的日志和监控信息
|
||||
|
||||
## 风险评估
|
||||
|
||||
- **性能影响**:增加重试机制可能会增加响应时间,但可以通过合理的超时设置来平衡
|
||||
- **内存使用**:增加失败响应的缓存可能会增加内存使用,但可以通过设置合理的缓存大小和过期时间来控制
|
||||
- **兼容性**:修改缓存机制可能会影响现有缓存的使用,但可以通过平滑过渡来避免问题
|
||||
Reference in New Issue
Block a user