270 lines
6.9 KiB
Markdown
270 lines
6.9 KiB
Markdown
# 问题修复 - WebSocket连接关闭错误 v3.13.5.2
|
||
|
||
## 📋 问题描述
|
||
|
||
### 错误信息
|
||
```
|
||
WebSocket connection to 'wss://...' failed:
|
||
WebSocket is closed before the connection is established.
|
||
```
|
||
|
||
### 错误含义
|
||
WebSocket 连接在**握手完成之前**就被关闭了。这是一个浏览器或网络层面的错误,不是应用层错误。
|
||
|
||
---
|
||
|
||
## 🔍 问题分析
|
||
|
||
### 主要原因
|
||
|
||
#### 1. 浏览器并发WebSocket连接限制 🔴
|
||
**原因:**
|
||
- 大多数浏览器对同一域名的 WebSocket 并发连接有限制
|
||
- Chrome/Edge: 通常限制为 **6-10个**
|
||
- Firefox: 通常限制为 **200个**(但建立速度有限制)
|
||
- Safari: 通常限制为 **6个**
|
||
|
||
**您的场景:**
|
||
- 900+ tokens 批量任务
|
||
- 连接池大小: 20(可能接近或超过某些浏览器限制)
|
||
- 当连接池快速创建连接时,浏览器可能拒绝部分连接
|
||
|
||
#### 2. 空闲超时设置过短 🟡
|
||
**原因:**
|
||
- 默认空闲超时: 30秒
|
||
- 批量任务可能需要更长时间执行
|
||
- 连接在任务完成前就被空闲超时关闭
|
||
|
||
**修复:**
|
||
```javascript
|
||
// ❌ 旧代码:缺少 idleTimeout 配置
|
||
const wsClient = new XyzwWebSocketClient({
|
||
url: finalWsUrl,
|
||
utils: g_utils,
|
||
heartbeatMs: 3000
|
||
})
|
||
|
||
// ✅ 新代码:设置5分钟空闲超时
|
||
const wsClient = new XyzwWebSocketClient({
|
||
url: finalWsUrl,
|
||
utils: g_utils,
|
||
heartbeatMs: 3000,
|
||
idleTimeout: 5 * 60 * 1000 // 5分钟
|
||
})
|
||
```
|
||
|
||
#### 3. 连接建立速度过快 🟡
|
||
**原因:**
|
||
- 连接池在短时间内创建多个连接
|
||
- 浏览器或服务器可能有速率限制
|
||
- 默认连接间隔: 300ms(可能太短)
|
||
|
||
---
|
||
|
||
## 🔧 修复方案
|
||
|
||
### 修复 1: 延长空闲超时时间 ✅
|
||
**文件:** `src/stores/tokenStore.js`
|
||
|
||
**修改:**
|
||
```javascript
|
||
idleTimeout: 5 * 60 * 1000 // 从30秒延长到5分钟
|
||
```
|
||
|
||
**效果:**
|
||
- ✅ 连接不会因为任务执行时间长而被关闭
|
||
- ✅ 给批量任务足够的执行时间
|
||
|
||
### 修复 2: 优化连接池配置 ⚠️
|
||
**建议配置:**
|
||
|
||
#### 对于 Chrome/Edge 用户:
|
||
```javascript
|
||
// 连接池配置(在批量任务页面)
|
||
连接池大小: 10(减少到浏览器限制以下)
|
||
连接间隔: 500ms(增加间隔,避免建立过快)
|
||
同时执行数: 5(保持不变)
|
||
```
|
||
|
||
#### 对于 Firefox 用户:
|
||
```javascript
|
||
// Firefox 连接限制较高,可以使用更大的连接池
|
||
连接池大小: 20(保持默认)
|
||
连接间隔: 300ms(保持默认)
|
||
同时执行数: 5(保持不变)
|
||
```
|
||
|
||
### 修复 3: 检查浏览器类型并推荐配置 💡
|
||
|
||
**在应用中添加浏览器检测和提示:**
|
||
```javascript
|
||
// 检测浏览器类型
|
||
const isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor)
|
||
const isFirefox = /Firefox/.test(navigator.userAgent)
|
||
|
||
if (isChrome && poolSize > 10) {
|
||
console.warn('⚠️ Chrome浏览器建议连接池大小不超过10,当前设置: ' + poolSize)
|
||
console.warn('💡 建议修改为10以避免连接被拒绝')
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 不同浏览器的限制
|
||
|
||
| 浏览器 | WebSocket并发限制 | 建议连接池大小 | 建议连接间隔 |
|
||
|--------|------------------|----------------|--------------|
|
||
| Chrome/Edge | 6-10个 | **≤10** | 500ms |
|
||
| Firefox | ~200个 | 20-50 | 300ms |
|
||
| Safari | ~6个 | **≤6** | 500ms |
|
||
| Opera | ~10个 | ≤10 | 500ms |
|
||
|
||
---
|
||
|
||
## 💡 使用建议
|
||
|
||
### 立即措施
|
||
1. **如果使用 Chrome/Edge:**
|
||
- 将连接池大小改为 **10**
|
||
- 将连接间隔改为 **500ms**
|
||
|
||
2. **如果使用 Firefox:**
|
||
- 可以保持连接池大小 **20**
|
||
- 连接间隔保持 **300ms**
|
||
|
||
3. **如果使用 Safari:**
|
||
- 将连接池大小改为 **6**
|
||
- 将连接间隔改为 **500ms**
|
||
|
||
### 长期方案
|
||
|
||
#### 方案A: 动态调整连接池大小(推荐)
|
||
```javascript
|
||
// 根据浏览器自动调整
|
||
const getOptimalPoolSize = () => {
|
||
const ua = navigator.userAgent
|
||
if (/Safari/.test(ua) && !/Chrome/.test(ua)) return 6 // Safari
|
||
if (/Firefox/.test(ua)) return 20 // Firefox
|
||
if (/Chrome|Edge/.test(ua)) return 10 // Chrome/Edge
|
||
return 10 // 默认
|
||
}
|
||
```
|
||
|
||
#### 方案B: 使用更保守的配置
|
||
```javascript
|
||
// 适用于所有浏览器的保守配置
|
||
连接池大小: 8
|
||
连接间隔: 500ms
|
||
同时执行数: 4
|
||
```
|
||
|
||
#### 方案C: 增加重试机制
|
||
```javascript
|
||
// 如果连接失败,等待后重试
|
||
if (error.message.includes('closed before the connection is established')) {
|
||
await sleep(1000) // 等待1秒
|
||
retry() // 重试连接
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🧪 测试建议
|
||
|
||
### 测试步骤
|
||
1. **调整连接池配置:**
|
||
- Chrome用户:连接池大小改为10
|
||
- 连接间隔改为500ms
|
||
|
||
2. **执行批量任务:**
|
||
- 选择100个tokens测试
|
||
- 观察是否还有连接关闭错误
|
||
|
||
3. **观察控制台:**
|
||
- 如果还有错误,进一步减小连接池或增加间隔
|
||
- 如果正常,可以尝试200、500、900个tokens
|
||
|
||
### 预期结果
|
||
- ✅ 不再出现 "closed before the connection is established" 错误
|
||
- ✅ 连接成功率提升
|
||
- ✅ 任务执行稳定
|
||
|
||
---
|
||
|
||
## 🔄 回退方案
|
||
|
||
如果修改后仍有问题,可以:
|
||
|
||
### 方案1: 禁用空闲超时
|
||
```javascript
|
||
idleTimeout: 0 // 禁用空闲超时
|
||
```
|
||
|
||
### 方案2: 使用极保守配置
|
||
```javascript
|
||
连接池大小: 5
|
||
连接间隔: 1000ms (1秒)
|
||
同时执行数: 3
|
||
```
|
||
|
||
### 方案3: 分批执行
|
||
```javascript
|
||
// 将900个tokens分成多批执行
|
||
// 每批100个,执行完一批再执行下一批
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 技术说明
|
||
|
||
### WebSocket连接生命周期
|
||
```
|
||
1. new WebSocket(url) ← 创建连接对象
|
||
2. 握手过程 ← 浏览器与服务器协商
|
||
3. onopen 事件 ← 连接建立成功
|
||
4. 可以发送/接收消息
|
||
5. onclose 事件 ← 连接关闭
|
||
```
|
||
|
||
**错误发生在步骤2:**
|
||
- 握手过程中,浏览器决定拒绝或关闭连接
|
||
- 通常是因为达到并发限制或资源不足
|
||
|
||
### 为什么不同浏览器限制不同?
|
||
- **Chrome/Safari:** 为了节省资源,限制较严格
|
||
- **Firefox:** 对 WebSocket 限制较宽松,但有其他性能限制
|
||
- **服务器端:** 也可能有连接数限制
|
||
|
||
---
|
||
|
||
## 📌 总结
|
||
|
||
### 已实施的修复 ✅
|
||
- [x] 延长空闲超时从30秒到5分钟
|
||
- [x] 添加 idleTimeout 配置到 WebSocket 客户端创建
|
||
|
||
### 需要用户操作 ⚠️
|
||
- [ ] 根据浏览器类型调整连接池大小
|
||
- [ ] 如果使用 Chrome/Edge,建议改为10
|
||
- [ ] 如果使用 Safari,建议改为6
|
||
- [ ] 测试并观察效果
|
||
|
||
### 后续优化方向 💡
|
||
- [ ] 添加浏览器检测和自动配置
|
||
- [ ] 添加连接失败重试机制
|
||
- [ ] 添加配置建议提示
|
||
|
||
---
|
||
|
||
## 🎯 版本信息
|
||
- **版本号:** v3.13.5.2
|
||
- **修复类型:** 性能优化 + 配置调整
|
||
- **影响范围:** WebSocket 连接管理
|
||
- **向后兼容:** ✅ 完全兼容
|
||
|
||
## 📚 相关文档
|
||
- [性能优化总结 v3.13.5](./性能优化总结v3.13.5.md)
|
||
- [性能全面检查报告 v3.13.5](./性能全面检查报告v3.13.5.md)
|
||
- [架构优化-100并发稳定运行方案 v3.13.0](./架构优化-100并发稳定运行方案v3.13.0.md)
|
||
|