diff --git a/internal/handler/metrics.go b/internal/handler/metrics.go index a5f3f81..fa93e95 100644 --- a/internal/handler/metrics.go +++ b/internal/handler/metrics.go @@ -32,6 +32,7 @@ type Metrics struct { LatencyPercentiles map[string]float64 `json:"latency_percentiles"` TopPaths []metrics.PathMetrics `json:"top_paths"` RecentRequests []metrics.RequestLog `json:"recent_requests"` + TopReferers []metrics.PathMetrics `json:"top_referers"` } func (h *ProxyHandler) MetricsHandler(w http.ResponseWriter, r *http.Request) { @@ -59,6 +60,7 @@ func (h *ProxyHandler) MetricsHandler(w http.ResponseWriter, r *http.Request) { StatusCodeStats: stats["status_code_stats"].(map[string]int64), TopPaths: stats["top_paths"].([]metrics.PathMetrics), RecentRequests: stats["recent_requests"].([]metrics.RequestLog), + TopReferers: stats["top_referers"].([]metrics.PathMetrics), } w.Header().Set("Content-Type", "application/json") @@ -259,68 +261,79 @@ var metricsTemplate = ` .status-3xx { background: #17a2b8; } .status-4xx { background: #ffc107; } .status-5xx { background: #dc3545; } + .grid-container { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 20px; + margin-bottom: 20px; + } + .grid-container .card { + margin-bottom: 0; + }

Proxy-Go Metrics

-
-

基础指标

-
- 运行时间 - +
+
+

基础指标

+
+ 运行时间 + +
+
+ 当前活跃请求 + +
+
+ 总请求数 + +
+
+ 错误数 + +
+
+ 错误率 + +
-
- 当前活跃请求 - -
-
- 总请求数 - -
-
- 错误数 - -
-
- 错误率 - -
-
-
-

系统指标

-
- Goroutine数量 - +
+

系统指标

+
+ Goroutine数量 + +
+
+ 内存使用 + +
-
- 内存使用 - -
-
-
-

性能指标

-
- 平均响应时间 - +
+

性能指标

+
+ 平均响应时间 + +
+
+ 每秒请求数 + +
-
- 每秒请求数 - -
-
-
-

流量统计

-
- 总传输字节 - -
-
- 每秒传输 - +
+

流量统计

+
+ 总传输字节 + +
+
+ 每秒传输 + +
@@ -362,6 +375,19 @@ var metricsTemplate = `
+
+

热门引用来源 (Top 10)

+ + + + + + + + +
来源请求数
+
+ @@ -452,6 +478,15 @@ var metricsTemplate = ` ).join(''); document.querySelector('#recentRequests tbody').innerHTML = recentRequestsHtml; + // 更新热门引用来源 + const topReferersHtml = data.top_referers.map(referer => + '' + + '' + referer.path + '' + + '' + referer.request_count + '' + + '' + ).join(''); + document.querySelector('#topReferers tbody').innerHTML = topReferersHtml; + document.getElementById('lastUpdate').textContent = '最后更新: ' + new Date().toLocaleTimeString(); } diff --git a/internal/handler/mirror_proxy.go b/internal/handler/mirror_proxy.go index 2fdb9ba..2a5b4cb 100644 --- a/internal/handler/mirror_proxy.go +++ b/internal/handler/mirror_proxy.go @@ -138,5 +138,5 @@ func (h *MirrorProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { utils.GetRequestSource(r), actualURL) // 记录统计信息 - collector.RecordRequest(r.URL.Path, resp.StatusCode, time.Since(startTime), bytesCopied, utils.GetClientIP(r)) + collector.RecordRequest(r.URL.Path, resp.StatusCode, time.Since(startTime), bytesCopied, utils.GetClientIP(r), r) } diff --git a/internal/handler/proxy.go b/internal/handler/proxy.go index 9f91d6b..f536571 100644 --- a/internal/handler/proxy.go +++ b/internal/handler/proxy.go @@ -109,7 +109,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - // 确定���标基础URL + // 确定标基础URL targetBase := pathConfig.DefaultTarget // 检查文件扩展名 @@ -228,7 +228,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } written, _ := w.Write(body) - collector.RecordRequest(r.URL.Path, resp.StatusCode, time.Since(start), int64(written), utils.GetClientIP(r)) + collector.RecordRequest(r.URL.Path, resp.StatusCode, time.Since(start), int64(written), utils.GetClientIP(r), r) } else { // 大响应使用流式传输 var bytesCopied int64 @@ -274,7 +274,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { targetURL, // 目标URL ) - collector.RecordRequest(r.URL.Path, resp.StatusCode, time.Since(start), bytesCopied, utils.GetClientIP(r)) + collector.RecordRequest(r.URL.Path, resp.StatusCode, time.Since(start), bytesCopied, utils.GetClientIP(r), r) } } diff --git a/internal/metrics/collector.go b/internal/metrics/collector.go index 03fca58..568bdea 100644 --- a/internal/metrics/collector.go +++ b/internal/metrics/collector.go @@ -2,6 +2,7 @@ package metrics import ( "fmt" + "net/http" "runtime" "sort" "sync" @@ -17,6 +18,7 @@ type Collector struct { totalBytes atomic.Int64 latencySum atomic.Int64 pathStats sync.Map + refererStats sync.Map statusStats [6]atomic.Int64 latencyBuckets [10]atomic.Int64 recentRequests struct { @@ -45,7 +47,7 @@ func (c *Collector) EndRequest() { atomic.AddInt64(&c.activeRequests, -1) } -func (c *Collector) RecordRequest(path string, status int, latency time.Duration, bytes int64, clientIP string) { +func (c *Collector) RecordRequest(path string, status int, latency time.Duration, bytes int64, clientIP string, r *http.Request) { // 更新总请求数 atomic.AddInt64(&c.totalRequests, 1) @@ -88,6 +90,17 @@ func (c *Collector) RecordRequest(path string, status int, latency time.Duration c.pathStats.Store(path, newStats) } + // 更新引用来源统计 + if referer := r.Header.Get("Referer"); referer != "" { + if stats, ok := c.refererStats.Load(referer); ok { + stats.(*PathStats).requests.Add(1) + } else { + newStats := &PathStats{} + newStats.requests.Add(1) + c.refererStats.Store(referer, newStats) + } + } + // 记录最近的请求 log := &RequestLog{ Time: time.Now(), @@ -151,6 +164,33 @@ func (c *Collector) GetStats() map[string]interface{} { pathMetrics = allPaths } + // 获取Top 10引用来源 + var refererMetrics []PathMetrics + var allReferers []PathMetrics + c.refererStats.Range(func(key, value interface{}) bool { + stats := value.(*PathStats) + if stats.requests.Load() == 0 { + return true + } + allReferers = append(allReferers, PathMetrics{ + Path: key.(string), + RequestCount: stats.requests.Load(), + }) + return true + }) + + // 按请求数排序 + sort.Slice(allReferers, func(i, j int) bool { + return allReferers[i].RequestCount > allReferers[j].RequestCount + }) + + // 取前10个 + if len(allReferers) > 10 { + refererMetrics = allReferers[:10] + } else { + refererMetrics = allReferers + } + return map[string]interface{}{ "uptime": uptime.String(), "active_requests": atomic.LoadInt64(&c.activeRequests), @@ -170,6 +210,7 @@ func (c *Collector) GetStats() map[string]interface{} { "status_code_stats": statusStats, "top_paths": pathMetrics, "recent_requests": c.getRecentRequests(), + "top_referers": refererMetrics, } } diff --git a/internal/middleware/fixed_path_proxy.go b/internal/middleware/fixed_path_proxy.go index 0b6365a..1b7ac60 100644 --- a/internal/middleware/fixed_path_proxy.go +++ b/internal/middleware/fixed_path_proxy.go @@ -83,7 +83,7 @@ func FixedPathProxyMiddleware(configs []config.FixedPathConfig) func(http.Handle } // 记录统计信息 - collector.RecordRequest(r.URL.Path, resp.StatusCode, time.Since(startTime), bytesCopied, utils.GetClientIP(r)) + collector.RecordRequest(r.URL.Path, resp.StatusCode, time.Since(startTime), bytesCopied, utils.GetClientIP(r), r) return }