mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-02 18:41:01 +08:00
feat(market): Add WebSocket stream limit protection with auto-downgrade
## Problem
Binance WebSocket has a hard limit of 1024 streams per connection.
Without protection:
- 300 coins × 4 timeframes = 1200 streams → Connection REJECTED
- System fails to start with "全市場掃描" mode
## Solution
Implement intelligent auto-downgrade mechanism with transparent logging.
### Changes to market/monitor.go (+42 lines)
**1. Add Constants**:
```go
const (
MaxStreamsPerConnection = 1024 // Binance hard limit
SafeMaxSymbols = 250 // Safe limit (97.7% usage, 2.3% buffer)
)
```
**2. Add Stream Count Check in Initialize()**:
- Calculate total streams: `len(symbols) × len(subKlineTime)`
- If exceeds 250 coins → Auto-downgrade to first 250
- Display detailed adjustment logs
**3. Add Usage Rate Display**:
```
✓ WebSocket 訂閱: X 個幣種 × 4 時間週期 = Y 流 (Z% 用量)
```
**4. Add High Usage Warning (>90%)**:
```
⚠️ 警告: 訂閱流使用率較高 (93.8%),建議減少幣種數量以確保穩定性
```
## Behavior by Scenario
| Coins | Original Streams | Adjusted Streams | Behavior |
|-------|-----------------|------------------|----------|
| 8 | 32 | 32 | ✅ Normal |
| 100 | 400 | 400 | ✅ Normal |
| 150 | 600 | 600 | ✅ Normal |
| 240 | 960 | 960 | ⚠️ Warning |
| 300 | 1200 | **1000** | 🔄 Auto-downgrade |
| 500 | 2000 | **1000** | 🔄 Auto-downgrade |
## Example Logs
### Normal Case (8 coins):
```
找到 8 个交易对
✓ WebSocket 訂閱: 8 個幣種 × 4 時間週期 = 32 流 (3.1% 用量)
```
### Auto-downgrade Case (300 coins):
```
找到 300 个交易对
⚠️ 幣種數量過多,自動調整:
- 原始數量: 300 個幣種 (1200 流)
- Binance 限制: 1024 流/連接
- 時間週期: 4 ([3m 15m 1h 4h])
- 調整後: 250 個幣種 (1000 流)
- 已過濾: 前 250 個幣種保留,其餘忽略
✓ WebSocket 訂閱: 250 個幣種 × 4 時間週期 = 1000 流 (97.7% 用量)
⚠️ 警告: 訂閱流使用率較高 (97.7%),建議減少幣種數量以確保穩定性
```
## Benefits
- ✅ System always starts successfully (no crashes)
- ✅ Transparent logging (users know what happened)
- ✅ Safe buffer (2.3% headroom for reconnections)
- ✅ No breaking changes (8-250 coins unchanged)
- ✅ Covers all realistic use cases (AI500 + OI Top = ~150 coins)
## Why 250, not 256?
- 256 × 4 = 1024 (no buffer, risky during reconnections)
- 250 × 4 = 1000 (24 stream buffer = 2.3% safety margin)
- Future-proof for potential 5th timeframe
Related: 15m/1h timeframe addition, WebSocket architecture merge
This commit is contained in:
@@ -9,6 +9,14 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxStreamsPerConnection Binance WebSocket 單連接最大訂閱流數限制
|
||||
MaxStreamsPerConnection = 1024
|
||||
// SafeMaxSymbols 安全的最大幣種數量(留 2.3% 緩衝空間)
|
||||
// 250 個幣種 × 4 時間週期 = 1000 流 < 1024
|
||||
SafeMaxSymbols = 250
|
||||
)
|
||||
|
||||
type WSMonitor struct {
|
||||
wsClient *WSClient
|
||||
combinedClient *CombinedStreamsClient
|
||||
@@ -69,6 +77,34 @@ func (m *WSMonitor) Initialize(coins []string) error {
|
||||
}
|
||||
|
||||
log.Printf("找到 %d 个交易对", len(m.symbols))
|
||||
|
||||
// WebSocket 訂閱流數檢查與自動調整
|
||||
totalStreams := len(m.symbols) * len(subKlineTime)
|
||||
|
||||
if len(m.symbols) > SafeMaxSymbols {
|
||||
log.Printf("⚠️ 幣種數量過多,自動調整:")
|
||||
log.Printf(" - 原始數量: %d 個幣種 (%d 流)", len(m.symbols), totalStreams)
|
||||
log.Printf(" - Binance 限制: %d 流/連接", MaxStreamsPerConnection)
|
||||
log.Printf(" - 時間週期: %d (%v)", len(subKlineTime), subKlineTime)
|
||||
|
||||
// 調整到安全上限
|
||||
m.symbols = m.symbols[:SafeMaxSymbols]
|
||||
totalStreams = len(m.symbols) * len(subKlineTime)
|
||||
|
||||
log.Printf(" - 調整後: %d 個幣種 (%d 流)", len(m.symbols), totalStreams)
|
||||
log.Printf(" - 已過濾: 前 %d 個幣種保留,其餘忽略", SafeMaxSymbols)
|
||||
}
|
||||
|
||||
// 顯示訂閱使用率
|
||||
usagePercent := float64(totalStreams) / float64(MaxStreamsPerConnection) * 100
|
||||
log.Printf("✓ WebSocket 訂閱: %d 個幣種 × %d 時間週期 = %d 流 (%.1f%% 用量)",
|
||||
len(m.symbols), len(subKlineTime), totalStreams, usagePercent)
|
||||
|
||||
// 接近上限警告(>90%)
|
||||
if usagePercent > 90 {
|
||||
log.Printf("⚠️ 警告: 訂閱流使用率較高 (%.1f%%),建議減少幣種數量以確保穩定性", usagePercent)
|
||||
}
|
||||
|
||||
// 初始化历史数据
|
||||
if err := m.initializeHistoricalData(); err != nil {
|
||||
log.Printf("初始化历史数据失败: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user