✨ 更新性能指标,优化内存使用和错误率显示,确保精度提升至两位小数;增加API超时时间配置以支持长时间流式响应
Browse files- .gitignore +1 -0
- main.go +39 -17
- stats.go +7 -3
.gitignore
CHANGED
|
@@ -108,3 +108,4 @@ api-proxy
|
|
| 108 |
# Go coverage files
|
| 109 |
*.out
|
| 110 |
proxy-server
|
|
|
|
|
|
| 108 |
# Go coverage files
|
| 109 |
*.out
|
| 110 |
proxy-server
|
| 111 |
+
deno-proxy
|
main.go
CHANGED
|
@@ -44,11 +44,12 @@ var (
|
|
| 44 |
"/cerebras": "https://api.cerebras.ai",
|
| 45 |
}
|
| 46 |
httpClient = &http.Client{
|
| 47 |
-
Timeout:
|
| 48 |
Transport: &http.Transport{
|
| 49 |
-
MaxIdleConns:
|
| 50 |
-
MaxIdleConnsPerHost:
|
| 51 |
-
IdleConnTimeout:
|
|
|
|
| 52 |
},
|
| 53 |
}
|
| 54 |
)
|
|
@@ -140,12 +141,6 @@ func (apc *AsyncProxyContext) StreamData(data []byte) error {
|
|
| 140 |
|
| 141 |
// handleAPIProxy 处理API代理请求(异步优化版)
|
| 142 |
func handleAPIProxy(c *gin.Context) {
|
| 143 |
-
// 创建异步代理上下文
|
| 144 |
-
asyncCtx := NewAsyncProxyContext(c, 60*time.Second) // 60秒超时
|
| 145 |
-
defer asyncCtx.cancel()
|
| 146 |
-
|
| 147 |
-
atomic.AddInt64(&requestCount, 1)
|
| 148 |
-
|
| 149 |
path := c.Request.URL.Path
|
| 150 |
prefix, rest := extractPrefixAndRest(path)
|
| 151 |
if prefix == "" {
|
|
@@ -154,6 +149,13 @@ func handleAPIProxy(c *gin.Context) {
|
|
| 154 |
return
|
| 155 |
}
|
| 156 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
// 异步记录请求,避免阻塞主流程
|
| 158 |
go stats.recordRequest(prefix)
|
| 159 |
|
|
@@ -299,13 +301,7 @@ func apc_streamResponseBody(asyncCtx *AsyncProxyContext, resp *http.Response) er
|
|
| 299 |
case <-asyncCtx.ctx.Done():
|
| 300 |
return asyncCtx.ctx.Err()
|
| 301 |
default:
|
| 302 |
-
// 设置读取超时
|
| 303 |
-
if deadline, ok := asyncCtx.ctx.Deadline(); ok {
|
| 304 |
-
if tcpConn, ok := resp.Body.(interface{ SetReadDeadline(time.Time) error }); ok {
|
| 305 |
-
tcpConn.SetReadDeadline(deadline)
|
| 306 |
-
}
|
| 307 |
-
}
|
| 308 |
-
|
| 309 |
n, err := resp.Body.Read(buffer)
|
| 310 |
if n > 0 {
|
| 311 |
if streamErr := asyncCtx.StreamData(buffer[:n]); streamErr != nil {
|
|
@@ -612,6 +608,31 @@ func fastJSONPatch(bodyBytes []byte) ([]byte, error) {
|
|
| 612 |
return json.Marshal(bodyJSON)
|
| 613 |
}
|
| 614 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 615 |
// main 主函数
|
| 616 |
func main() {
|
| 617 |
// 设置生产模式
|
|
@@ -671,6 +692,7 @@ func main() {
|
|
| 671 |
log.Printf("🚀 API代理服务器已启动 (Go优化版) 端口:%s", port)
|
| 672 |
log.Printf("🕒 统计数据每分钟自动刷新页面")
|
| 673 |
log.Printf("⚡ 性能优化:异步统计、内存优化、锁竞争减少")
|
|
|
|
| 674 |
log.Printf("📊 访问 http://localhost:%s 查看统计信息", port)
|
| 675 |
|
| 676 |
// 使用自定义HTTP服务器以更好地控制
|
|
|
|
| 44 |
"/cerebras": "https://api.cerebras.ai",
|
| 45 |
}
|
| 46 |
httpClient = &http.Client{
|
| 47 |
+
Timeout: 1800 * time.Second, // 增加到30分钟,适合长时间AI流式响应
|
| 48 |
Transport: &http.Transport{
|
| 49 |
+
MaxIdleConns: 100,
|
| 50 |
+
MaxIdleConnsPerHost: 100,
|
| 51 |
+
IdleConnTimeout: 90 * time.Second,
|
| 52 |
+
ResponseHeaderTimeout: 30 * time.Second, // 只对响应头设置30秒超时
|
| 53 |
},
|
| 54 |
}
|
| 55 |
)
|
|
|
|
| 141 |
|
| 142 |
// handleAPIProxy 处理API代理请求(异步优化版)
|
| 143 |
func handleAPIProxy(c *gin.Context) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
path := c.Request.URL.Path
|
| 145 |
prefix, rest := extractPrefixAndRest(path)
|
| 146 |
if prefix == "" {
|
|
|
|
| 149 |
return
|
| 150 |
}
|
| 151 |
|
| 152 |
+
// 根据API端点设置合适的超时时间
|
| 153 |
+
timeout := getTimeoutForEndpoint(prefix)
|
| 154 |
+
asyncCtx := NewAsyncProxyContext(c, timeout)
|
| 155 |
+
defer asyncCtx.cancel()
|
| 156 |
+
|
| 157 |
+
atomic.AddInt64(&requestCount, 1)
|
| 158 |
+
|
| 159 |
// 异步记录请求,避免阻塞主流程
|
| 160 |
go stats.recordRequest(prefix)
|
| 161 |
|
|
|
|
| 301 |
case <-asyncCtx.ctx.Done():
|
| 302 |
return asyncCtx.ctx.Err()
|
| 303 |
default:
|
| 304 |
+
// 对于流式响应,不设置严格的读取超时,让context控制整体超时
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
n, err := resp.Body.Read(buffer)
|
| 306 |
if n > 0 {
|
| 307 |
if streamErr := asyncCtx.StreamData(buffer[:n]); streamErr != nil {
|
|
|
|
| 608 |
return json.Marshal(bodyJSON)
|
| 609 |
}
|
| 610 |
|
| 611 |
+
// getTimeoutForEndpoint 根据API端点返回合适的超时时间
|
| 612 |
+
func getTimeoutForEndpoint(prefix string) time.Duration {
|
| 613 |
+
// AI流式API需要更长的超时时间 - 30分钟
|
| 614 |
+
aiEndpoints := map[string]time.Duration{
|
| 615 |
+
"/openai": 1800 * time.Second, // 30分钟
|
| 616 |
+
"/claude": 1800 * time.Second, // 30分钟
|
| 617 |
+
"/gemini": 1800 * time.Second, // 30分钟
|
| 618 |
+
"/gnothink": 1800 * time.Second, // 30分钟
|
| 619 |
+
"/groq": 1800 * time.Second, // 30分钟
|
| 620 |
+
"/xai": 1800 * time.Second, // 30分钟
|
| 621 |
+
"/cohere": 1800 * time.Second, // 30分钟
|
| 622 |
+
"/together": 1800 * time.Second, // 30分钟
|
| 623 |
+
"/fireworks": 1800 * time.Second, // 30分钟
|
| 624 |
+
"/openrouter": 1800 * time.Second, // 30分钟
|
| 625 |
+
"/cerebras": 1800 * time.Second, // 30分钟
|
| 626 |
+
}
|
| 627 |
+
|
| 628 |
+
if timeout, exists := aiEndpoints[prefix]; exists {
|
| 629 |
+
return timeout
|
| 630 |
+
}
|
| 631 |
+
|
| 632 |
+
// 其他API使用较短的超时时间
|
| 633 |
+
return 60 * time.Second
|
| 634 |
+
}
|
| 635 |
+
|
| 636 |
// main 主函数
|
| 637 |
func main() {
|
| 638 |
// 设置生产模式
|
|
|
|
| 692 |
log.Printf("🚀 API代理服务器已启动 (Go优化版) 端口:%s", port)
|
| 693 |
log.Printf("🕒 统计数据每分钟自动刷新页面")
|
| 694 |
log.Printf("⚡ 性能优化:异步统计、内存优化、锁竞争减少")
|
| 695 |
+
log.Printf("⏱️ 超时配置:AI API 30分钟,其他API 1分钟,HTTP客户端 30分钟")
|
| 696 |
log.Printf("📊 访问 http://localhost:%s 查看统计信息", port)
|
| 697 |
|
| 698 |
// 使用自定义HTTP服务器以更好地控制
|
stats.go
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
package main
|
| 2 |
|
| 3 |
import (
|
|
|
|
| 4 |
"runtime"
|
| 5 |
"sync"
|
| 6 |
"sync/atomic"
|
|
@@ -241,7 +242,7 @@ func updatePerformanceMetrics() {
|
|
| 241 |
var m runtime.MemStats
|
| 242 |
runtime.ReadMemStats(&m)
|
| 243 |
|
| 244 |
-
perfMetrics.MemoryUsageMB = float64(m.Alloc)
|
| 245 |
perfMetrics.GoroutineCount = runtime.NumGoroutine()
|
| 246 |
perfMetrics.LastUpdated = now.UnixMilli()
|
| 247 |
|
|
@@ -281,6 +282,9 @@ func updatePerformanceMetrics() {
|
|
| 281 |
perfMetrics.RequestsPerSec = 0.3*qps + 0.7*perfMetrics.RequestsPerSec
|
| 282 |
}
|
| 283 |
|
|
|
|
|
|
|
|
|
|
| 284 |
// 更新记录
|
| 285 |
atomic.StoreInt64(&lastQPSUpdate, currentTime)
|
| 286 |
atomic.StoreInt64(&lastRequestCount, currentRequests)
|
|
@@ -291,8 +295,8 @@ func updatePerformanceMetrics() {
|
|
| 291 |
totalErrors := atomic.LoadInt64(&errorCount)
|
| 292 |
if totalReqs > 0 {
|
| 293 |
errorRate := float64(totalErrors) / float64(totalReqs) * 100
|
| 294 |
-
//
|
| 295 |
-
perfMetrics.ErrorRate =
|
| 296 |
}
|
| 297 |
|
| 298 |
// 计算平均响应时间
|
|
|
|
| 1 |
package main
|
| 2 |
|
| 3 |
import (
|
| 4 |
+
"math"
|
| 5 |
"runtime"
|
| 6 |
"sync"
|
| 7 |
"sync/atomic"
|
|
|
|
| 242 |
var m runtime.MemStats
|
| 243 |
runtime.ReadMemStats(&m)
|
| 244 |
|
| 245 |
+
perfMetrics.MemoryUsageMB = math.Round(float64(m.Alloc)/1024/1024*100) / 100
|
| 246 |
perfMetrics.GoroutineCount = runtime.NumGoroutine()
|
| 247 |
perfMetrics.LastUpdated = now.UnixMilli()
|
| 248 |
|
|
|
|
| 282 |
perfMetrics.RequestsPerSec = 0.3*qps + 0.7*perfMetrics.RequestsPerSec
|
| 283 |
}
|
| 284 |
|
| 285 |
+
// 保留2位小数
|
| 286 |
+
perfMetrics.RequestsPerSec = math.Round(perfMetrics.RequestsPerSec*100) / 100
|
| 287 |
+
|
| 288 |
// 更新记录
|
| 289 |
atomic.StoreInt64(&lastQPSUpdate, currentTime)
|
| 290 |
atomic.StoreInt64(&lastRequestCount, currentRequests)
|
|
|
|
| 295 |
totalErrors := atomic.LoadInt64(&errorCount)
|
| 296 |
if totalReqs > 0 {
|
| 297 |
errorRate := float64(totalErrors) / float64(totalReqs) * 100
|
| 298 |
+
// 使用math.Round保证精确的2位小数
|
| 299 |
+
perfMetrics.ErrorRate = math.Round(errorRate*100) / 100
|
| 300 |
}
|
| 301 |
|
| 302 |
// 计算平均响应时间
|