更新Swagger API文档
This commit is contained in:
276
dns/server.go
276
dns/server.go
@@ -8,7 +8,9 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -33,6 +35,8 @@ type StatsData struct {
|
||||
BlockedDomains map[string]*BlockedDomain `json:"blockedDomains"`
|
||||
ResolvedDomains map[string]*BlockedDomain `json:"resolvedDomains"`
|
||||
HourlyStats map[string]int64 `json:"hourlyStats"`
|
||||
DailyStats map[string]int64 `json:"dailyStats"`
|
||||
MonthlyStats map[string]int64 `json:"monthlyStats"`
|
||||
LastSaved time.Time `json:"lastSaved"`
|
||||
}
|
||||
|
||||
@@ -53,17 +57,27 @@ type Server struct {
|
||||
resolvedDomains map[string]*BlockedDomain // 用于记录解析的域名
|
||||
hourlyStatsMutex sync.RWMutex
|
||||
hourlyStats map[string]int64 // 按小时统计屏蔽数量
|
||||
dailyStatsMutex sync.RWMutex
|
||||
dailyStats map[string]int64 // 按天统计屏蔽数量
|
||||
monthlyStatsMutex sync.RWMutex
|
||||
monthlyStats map[string]int64 // 按月统计屏蔽数量
|
||||
saveTicker *time.Ticker // 用于定时保存数据
|
||||
startTime time.Time // 服务器启动时间
|
||||
saveDone chan struct{} // 用于通知保存协程停止
|
||||
}
|
||||
|
||||
// Stats DNS服务器统计信息
|
||||
type Stats struct {
|
||||
Queries int64
|
||||
Blocked int64
|
||||
Allowed int64
|
||||
Errors int64
|
||||
LastQuery time.Time
|
||||
Queries int64
|
||||
Blocked int64
|
||||
Allowed int64
|
||||
Errors int64
|
||||
LastQuery time.Time
|
||||
AvgResponseTime float64 // 平均响应时间(ms)
|
||||
TotalResponseTime int64 // 总响应时间
|
||||
QueryTypes map[string]int64 // 查询类型统计
|
||||
SourceIPs map[string]bool // 活跃来源IP
|
||||
CpuUsage float64 // CPU使用率(%)
|
||||
}
|
||||
|
||||
// NewServer 创建DNS服务器实例
|
||||
@@ -77,17 +91,25 @@ func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shie
|
||||
Net: "udp",
|
||||
Timeout: time.Duration(config.Timeout) * time.Millisecond,
|
||||
},
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
startTime: time.Now(), // 记录服务器启动时间
|
||||
stats: &Stats{
|
||||
Queries: 0,
|
||||
Blocked: 0,
|
||||
Allowed: 0,
|
||||
Errors: 0,
|
||||
Queries: 0,
|
||||
Blocked: 0,
|
||||
Allowed: 0,
|
||||
Errors: 0,
|
||||
AvgResponseTime: 0,
|
||||
TotalResponseTime: 0,
|
||||
QueryTypes: make(map[string]int64),
|
||||
SourceIPs: make(map[string]bool),
|
||||
CpuUsage: 0,
|
||||
},
|
||||
blockedDomains: make(map[string]*BlockedDomain),
|
||||
resolvedDomains: make(map[string]*BlockedDomain),
|
||||
hourlyStats: make(map[string]int64),
|
||||
dailyStats: make(map[string]int64),
|
||||
monthlyStats: make(map[string]int64),
|
||||
saveDone: make(chan struct{}),
|
||||
}
|
||||
|
||||
@@ -113,6 +135,9 @@ func (s *Server) Start() error {
|
||||
Handler: dns.HandlerFunc(s.handleDNSRequest),
|
||||
}
|
||||
|
||||
// 启动CPU使用率监控
|
||||
go s.startCpuUsageMonitor()
|
||||
|
||||
// 启动UDP服务
|
||||
go func() {
|
||||
logger.Info(fmt.Sprintf("DNS UDP服务器启动,监听端口: %d", s.config.Port))
|
||||
@@ -154,9 +179,20 @@ func (s *Server) Stop() {
|
||||
|
||||
// handleDNSRequest 处理DNS请求
|
||||
func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
|
||||
startTime := time.Now()
|
||||
|
||||
// 获取来源IP
|
||||
sourceIP := w.RemoteAddr().String()
|
||||
// 提取IP地址部分,去掉端口
|
||||
if idx := strings.LastIndex(sourceIP, ":"); idx >= 0 {
|
||||
sourceIP = sourceIP[:idx]
|
||||
}
|
||||
|
||||
// 更新来源IP统计
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.Queries++
|
||||
stats.LastQuery = time.Now()
|
||||
stats.SourceIPs[sourceIP] = true
|
||||
})
|
||||
|
||||
// 只处理递归查询
|
||||
@@ -166,35 +202,75 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
|
||||
response.RecursionAvailable = true
|
||||
response.SetRcode(r, dns.RcodeRefused)
|
||||
w.WriteMsg(response)
|
||||
|
||||
// 计算响应时间
|
||||
responseTime := time.Since(startTime).Milliseconds()
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.TotalResponseTime += responseTime
|
||||
if stats.Queries > 0 {
|
||||
stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 获取查询域名
|
||||
// 获取查询域名和类型
|
||||
var domain string
|
||||
var queryType string
|
||||
if len(r.Question) > 0 {
|
||||
domain = r.Question[0].Name
|
||||
// 移除末尾的点
|
||||
if len(domain) > 0 && domain[len(domain)-1] == '.' {
|
||||
domain = domain[:len(domain)-1]
|
||||
}
|
||||
// 获取查询类型
|
||||
queryType = dns.TypeToString[r.Question[0].Qtype]
|
||||
// 更新查询类型统计
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.QueryTypes[queryType]++
|
||||
})
|
||||
}
|
||||
|
||||
logger.Debug("接收到DNS查询", "domain", domain, "type", r.Question[0].Qtype, "client", w.RemoteAddr())
|
||||
logger.Debug("接收到DNS查询", "domain", domain, "type", queryType, "client", w.RemoteAddr())
|
||||
|
||||
// 检查hosts文件是否有匹配
|
||||
if ip, exists := s.shieldManager.GetHostsIP(domain); exists {
|
||||
s.handleHostsResponse(w, r, ip)
|
||||
// 计算响应时间
|
||||
responseTime := time.Since(startTime).Milliseconds()
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.TotalResponseTime += responseTime
|
||||
if stats.Queries > 0 {
|
||||
stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否被屏蔽
|
||||
if s.shieldManager.IsBlocked(domain) {
|
||||
s.handleBlockedResponse(w, r, domain)
|
||||
// 计算响应时间
|
||||
responseTime := time.Since(startTime).Milliseconds()
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.TotalResponseTime += responseTime
|
||||
if stats.Queries > 0 {
|
||||
stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 转发到上游DNS服务器
|
||||
s.forwardDNSRequest(w, r, domain)
|
||||
// 计算响应时间
|
||||
responseTime := time.Since(startTime).Milliseconds()
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.TotalResponseTime += responseTime
|
||||
if stats.Queries > 0 {
|
||||
stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// handleHostsResponse 处理hosts文件匹配的响应
|
||||
@@ -354,11 +430,26 @@ func (s *Server) updateBlockedDomainStats(domain string) {
|
||||
}
|
||||
}
|
||||
|
||||
// 更新统计数据
|
||||
now := time.Now()
|
||||
|
||||
// 更新小时统计
|
||||
hourKey := time.Now().Format("2006-01-02-15")
|
||||
hourKey := now.Format("2006-01-02-15")
|
||||
s.hourlyStatsMutex.Lock()
|
||||
s.hourlyStats[hourKey]++
|
||||
s.hourlyStatsMutex.Unlock()
|
||||
|
||||
// 更新每日统计
|
||||
dayKey := now.Format("2006-01-02")
|
||||
s.dailyStatsMutex.Lock()
|
||||
s.dailyStats[dayKey]++
|
||||
s.dailyStatsMutex.Unlock()
|
||||
|
||||
// 更新每月统计
|
||||
monthKey := now.Format("2006-01")
|
||||
s.monthlyStatsMutex.Lock()
|
||||
s.monthlyStats[monthKey]++
|
||||
s.monthlyStatsMutex.Unlock()
|
||||
}
|
||||
|
||||
// updateResolvedDomainStats 更新解析域名统计
|
||||
@@ -385,18 +476,40 @@ func (s *Server) updateStats(update func(*Stats)) {
|
||||
update(s.stats)
|
||||
}
|
||||
|
||||
// GetStartTime 获取服务器启动时间
|
||||
func (s *Server) GetStartTime() time.Time {
|
||||
return s.startTime
|
||||
}
|
||||
|
||||
// GetStats 获取DNS服务器统计信息
|
||||
func (s *Server) GetStats() *Stats {
|
||||
s.statsMutex.Lock()
|
||||
defer s.statsMutex.Unlock()
|
||||
|
||||
// 复制查询类型统计
|
||||
queryTypesCopy := make(map[string]int64)
|
||||
for k, v := range s.stats.QueryTypes {
|
||||
queryTypesCopy[k] = v
|
||||
}
|
||||
|
||||
// 复制来源IP统计
|
||||
sourceIPsCopy := make(map[string]bool)
|
||||
for ip := range s.stats.SourceIPs {
|
||||
sourceIPsCopy[ip] = true
|
||||
}
|
||||
|
||||
// 返回统计信息的副本
|
||||
return &Stats{
|
||||
Queries: s.stats.Queries,
|
||||
Blocked: s.stats.Blocked,
|
||||
Allowed: s.stats.Allowed,
|
||||
Errors: s.stats.Errors,
|
||||
LastQuery: s.stats.LastQuery,
|
||||
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: queryTypesCopy,
|
||||
SourceIPs: sourceIPsCopy,
|
||||
CpuUsage: s.stats.CpuUsage,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,7 +582,7 @@ func (s *Server) GetRecentBlockedDomains(limit int) []BlockedDomain {
|
||||
return domains
|
||||
}
|
||||
|
||||
// GetHourlyStats 获取24小时屏蔽统计
|
||||
// GetHourlyStats 获取每小时统计数据
|
||||
func (s *Server) GetHourlyStats() map[string]int64 {
|
||||
s.hourlyStatsMutex.RLock()
|
||||
defer s.hourlyStatsMutex.RUnlock()
|
||||
@@ -482,6 +595,32 @@ func (s *Server) GetHourlyStats() map[string]int64 {
|
||||
return result
|
||||
}
|
||||
|
||||
// GetDailyStats 获取每日统计数据
|
||||
func (s *Server) GetDailyStats() map[string]int64 {
|
||||
s.dailyStatsMutex.RLock()
|
||||
defer s.dailyStatsMutex.RUnlock()
|
||||
|
||||
// 返回副本
|
||||
result := make(map[string]int64)
|
||||
for k, v := range s.dailyStats {
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetMonthlyStats 获取每月统计数据
|
||||
func (s *Server) GetMonthlyStats() map[string]int64 {
|
||||
s.monthlyStatsMutex.RLock()
|
||||
defer s.monthlyStatsMutex.RUnlock()
|
||||
|
||||
// 返回副本
|
||||
result := make(map[string]int64)
|
||||
for k, v := range s.monthlyStats {
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// loadStatsData 从文件加载统计数据
|
||||
func (s *Server) loadStatsData() {
|
||||
if s.config.StatsFile == "" {
|
||||
@@ -528,6 +667,18 @@ func (s *Server) loadStatsData() {
|
||||
s.hourlyStats = statsData.HourlyStats
|
||||
}
|
||||
s.hourlyStatsMutex.Unlock()
|
||||
|
||||
s.dailyStatsMutex.Lock()
|
||||
if statsData.DailyStats != nil {
|
||||
s.dailyStats = statsData.DailyStats
|
||||
}
|
||||
s.dailyStatsMutex.Unlock()
|
||||
|
||||
s.monthlyStatsMutex.Lock()
|
||||
if statsData.MonthlyStats != nil {
|
||||
s.monthlyStats = statsData.MonthlyStats
|
||||
}
|
||||
s.monthlyStatsMutex.Unlock()
|
||||
|
||||
logger.Info("统计数据加载成功")
|
||||
}
|
||||
@@ -573,6 +724,20 @@ func (s *Server) saveStatsData() {
|
||||
statsData.HourlyStats[k] = v
|
||||
}
|
||||
s.hourlyStatsMutex.RUnlock()
|
||||
|
||||
s.dailyStatsMutex.RLock()
|
||||
statsData.DailyStats = make(map[string]int64)
|
||||
for k, v := range s.dailyStats {
|
||||
statsData.DailyStats[k] = v
|
||||
}
|
||||
s.dailyStatsMutex.RUnlock()
|
||||
|
||||
s.monthlyStatsMutex.RLock()
|
||||
statsData.MonthlyStats = make(map[string]int64)
|
||||
for k, v := range s.monthlyStats {
|
||||
statsData.MonthlyStats[k] = v
|
||||
}
|
||||
s.monthlyStatsMutex.RUnlock()
|
||||
|
||||
// 序列化数据
|
||||
jsonData, err := json.MarshalIndent(statsData, "", " ")
|
||||
@@ -591,6 +756,77 @@ func (s *Server) saveStatsData() {
|
||||
logger.Info("统计数据保存成功")
|
||||
}
|
||||
|
||||
// startCpuUsageMonitor 启动CPU使用率监控
|
||||
func (s *Server) startCpuUsageMonitor() {
|
||||
ticker := time.NewTicker(time.Second * 5) // 每5秒更新一次CPU使用率
|
||||
defer ticker.Stop()
|
||||
|
||||
// 初始化
|
||||
var memStats runtime.MemStats
|
||||
runtime.ReadMemStats(&memStats)
|
||||
|
||||
// 存储上一次的CPU时间统计
|
||||
var prevIdle, prevTotal uint64
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// 获取真实的系统级CPU使用率
|
||||
cpuUsage, err := getSystemCpuUsage(&prevIdle, &prevTotal)
|
||||
if err != nil {
|
||||
// 如果获取失败,使用默认值
|
||||
cpuUsage = 0.0
|
||||
logger.Error("获取系统CPU使用率失败", "error", err)
|
||||
}
|
||||
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.CpuUsage = cpuUsage
|
||||
})
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getSystemCpuUsage 获取系统CPU使用率
|
||||
func getSystemCpuUsage(prevIdle, prevTotal *uint64) (float64, error) {
|
||||
// 读取/proc/stat文件获取CPU统计信息
|
||||
file, err := os.Open("/proc/stat")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var cpuUser, cpuNice, cpuSystem, cpuIdle, cpuIowait, cpuIrq, cpuSoftirq, cpuSteal uint64
|
||||
_, err = fmt.Fscanf(file, "cpu %d %d %d %d %d %d %d %d",
|
||||
&cpuUser, &cpuNice, &cpuSystem, &cpuIdle, &cpuIowait, &cpuIrq, &cpuSoftirq, &cpuSteal)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// 计算总的CPU时间
|
||||
total := cpuUser + cpuNice + cpuSystem + cpuIdle + cpuIowait + cpuIrq + cpuSoftirq + cpuSteal
|
||||
idle := cpuIdle + cpuIowait
|
||||
|
||||
// 第一次调用时,只初始化值,不计算使用率
|
||||
if *prevTotal == 0 || *prevIdle == 0 {
|
||||
*prevIdle = idle
|
||||
*prevTotal = total
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// 计算CPU使用率
|
||||
idleDelta := idle - *prevIdle
|
||||
totalDelta := total - *prevTotal
|
||||
utilization := float64(totalDelta-idleDelta) / float64(totalDelta) * 100
|
||||
|
||||
// 更新上一次的值
|
||||
*prevIdle = idle
|
||||
*prevTotal = total
|
||||
|
||||
return utilization, nil
|
||||
}
|
||||
|
||||
// startAutoSave 启动自动保存功能
|
||||
func (s *Server) startAutoSave() {
|
||||
if s.config.StatsFile == "" || s.config.SaveInterval <= 0 {
|
||||
|
||||
Reference in New Issue
Block a user