581 lines
15 KiB
Markdown
581 lines
15 KiB
Markdown
# RunningHub 并发能力分析与优化方案
|
||
|
||
**版本:** v2.1.0
|
||
**更新时间:** 2025-10-20
|
||
**分析人员:** AI架构团队
|
||
|
||
---
|
||
|
||
## 📊 一、RunningHub API并发能力评估
|
||
|
||
### 1.1 API架构分析
|
||
|
||
RunningHub采用**异步任务处理模式**,这种架构天然支持高并发:
|
||
|
||
```
|
||
客户端请求 → 提交任务(秒级响应) → 返回TaskID → 客户端轮询 → 获取结果
|
||
```
|
||
|
||
**优势:**
|
||
- ✅ 提交接口无需等待任务完成,可快速响应
|
||
- ✅ 任务在后台队列中处理,不占用HTTP连接
|
||
- ✅ 理论上可同时提交大量任务
|
||
|
||
---
|
||
|
||
### 1.2 并发限制因素
|
||
|
||
#### A. API Key限制(未知,需测试)
|
||
|
||
RunningHub未公开以下限制:
|
||
- ❓ 每秒最大请求数(QPS)
|
||
- ❓ 每分钟最大请求数(QPM)
|
||
- ❓ 单个API Key的并发任务数
|
||
- ❓ 账户级别的任务队列限制
|
||
|
||
**建议测试方案:**
|
||
```bash
|
||
# 逐步压力测试
|
||
1. 同时提交10个任务 → 观察响应
|
||
2. 同时提交50个任务 → 观察是否限流
|
||
3. 同时提交100个任务 → 找到阈值
|
||
```
|
||
|
||
#### B. 任务处理能力
|
||
|
||
根据API文档分析:
|
||
- **文生视频(10秒):** 预计处理时间 2-5分钟
|
||
- **图生视频(10秒):** 预计处理时间 2-5分钟
|
||
- **高清视频:** 预计处理时间 5-10分钟
|
||
|
||
**并发处理能力估算:**
|
||
假设RunningHub后台有100个GPU实例,平均处理时间3分钟:
|
||
```
|
||
理论最大并发 = 100个GPU × (60秒 / 3分钟) = 约2000个任务/小时
|
||
```
|
||
|
||
#### C. 网络带宽限制
|
||
|
||
- **请求体大小:**
|
||
- 文生视频:~2KB(prompt + 配置)
|
||
- 图生视频:~10KB-500KB(包含图片URL或Base64)
|
||
|
||
- **响应体大小:**
|
||
- 提交响应:~500B
|
||
- 状态查询:~300B
|
||
- 结果获取:~1KB(只返回URL)
|
||
|
||
**预计带宽需求(100并发):**
|
||
```
|
||
上传:100 × 500KB = 50MB
|
||
下载(轮询10次/任务):100 × 10 × 1KB = 1MB
|
||
总计:~51MB(一次性峰值)
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 二、当前系统并发配置
|
||
|
||
### 2.1 系统参数
|
||
|
||
```yaml
|
||
# application.yml
|
||
ai:
|
||
providers:
|
||
runninghub:
|
||
polling-interval: 10000 # 轮询间隔 10秒(已优化)
|
||
max-polling-times: 60 # 最大轮询60次 = 10分钟
|
||
queue:
|
||
max-concurrent: 50 # 每个模型最大并发50个
|
||
```
|
||
|
||
### 2.2 并发能力计算
|
||
|
||
#### A. 系统级并发
|
||
|
||
**RunningHub模型数量:** 12个
|
||
```
|
||
rh_sora2_text_portrait (文生视频-竖屏)
|
||
rh_sora2_text_landscape (文生视频-横屏)
|
||
rh_sora2_text_portrait_hd (文生视频-高清竖屏)
|
||
rh_sora2_text_landscape_hd (文生视频-高清横屏)
|
||
rh_sora2_text_portrait_15s (文生视频-竖屏15秒)
|
||
rh_sora2_text_landscape_15s (文生视频-横屏15秒)
|
||
rh_sora2_img_portrait (图生视频-竖屏)
|
||
rh_sora2_img_landscape (图生视频-横屏)
|
||
rh_sora2_img_portrait_hd (图生视频-高清竖屏)
|
||
rh_sora2_img_landscape_hd (图生视频-高清横屏)
|
||
rh_sora2_img_portrait_15s (图生视频-竖屏15秒)
|
||
rh_sora2_img_landscape_15s (图生视频-横屏15秒)
|
||
```
|
||
|
||
**理论最大并发:**
|
||
```
|
||
12个模型 × 50个/模型 = 600个并发任务
|
||
```
|
||
|
||
**但实际上:**
|
||
- RunningHub任务不走我们的队列系统(直接提交)
|
||
- 只受RunningHub API限制,不受我们的max-concurrent限制
|
||
|
||
**实际并发能力:** **无限制**(取决于RunningHub服务端)
|
||
|
||
#### B. 轮询调度器负载
|
||
|
||
```java
|
||
@Scheduled(fixedRateString = "${ai.providers.runninghub.polling-interval:10000}")
|
||
public void pollRunningHubTasks() {
|
||
// 查询所有processing状态的RunningHub任务
|
||
List<AiTask> processingTasks = aiTaskMapper.findProcessingTasksByProvider("runninghub");
|
||
|
||
// 每10秒轮询一次
|
||
for (AiTask task : processingTasks) {
|
||
// 查询状态 → 更新数据库 → 发送WebSocket通知
|
||
}
|
||
}
|
||
```
|
||
|
||
**轮询负载分析(10秒间隔):**
|
||
|
||
| 并发任务数 | 每次轮询耗时 | CPU使用率 | 网络流量/分钟 |
|
||
|-----------|------------|----------|--------------|
|
||
| 10 | ~1秒 | <5% | ~180KB |
|
||
| 50 | ~5秒 | ~10% | ~900KB |
|
||
| 100 | ~10秒 | ~20% | ~1.8MB |
|
||
| 200 | ~20秒 | ~40% | ~3.6MB |
|
||
| 500 | ~50秒 | ~80% | ~9MB |
|
||
|
||
**安全阈值:** 建议不超过**200个并发任务**,否则轮询调度器可能积压
|
||
|
||
---
|
||
|
||
## ⚠️ 三、潜在风险与瓶颈
|
||
|
||
### 3.1 轮询调度器阻塞
|
||
|
||
**问题:** 如果并发任务过多,单次轮询时间超过10秒,会导致任务堆积。
|
||
|
||
**示例:**
|
||
```
|
||
T0: 开始轮询200个任务(预计20秒)
|
||
T10: 第二次调度触发,但上一次还没结束 → 跳过或阻塞
|
||
T20: 第一次轮询完成
|
||
T30: 第三次调度触发
|
||
```
|
||
|
||
**解决方案:**
|
||
1. 使用 `@Scheduled(fixedDelay = 10000)` 替代 `fixedRate`
|
||
2. 增加线程池,并发查询状态
|
||
|
||
### 3.2 数据库连接池耗尽
|
||
|
||
**当前连接池配置(默认):** HikariCP 默认10个连接
|
||
|
||
**风险:**
|
||
```
|
||
200个并发任务 × 每个任务3次数据库操作(查询+更新+日志) = 600次DB操作/10秒
|
||
```
|
||
|
||
**解决方案:**
|
||
```yaml
|
||
spring:
|
||
datasource:
|
||
hikari:
|
||
maximum-pool-size: 50 # 增加连接池大小
|
||
minimum-idle: 10
|
||
connection-timeout: 30000
|
||
```
|
||
|
||
### 3.3 WebSocket连接数
|
||
|
||
**问题:** 每个在线用户需要一个WebSocket连接来接收实时通知。
|
||
|
||
**并发用户数 vs WebSocket连接:**
|
||
```
|
||
100个并发用户 = 100个WebSocket连接
|
||
500个并发用户 = 500个WebSocket连接
|
||
```
|
||
|
||
**Tomcat默认限制:** 200个线程
|
||
|
||
**解决方案:**
|
||
```yaml
|
||
server:
|
||
tomcat:
|
||
threads:
|
||
max: 500 # 增加最大线程数
|
||
min-spare: 50
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 四、并发优化方案
|
||
|
||
### 方案A:保守配置(推荐用于初期)
|
||
|
||
**目标:** 稳定性优先,支持 **100个并发任务**
|
||
|
||
```yaml
|
||
ai:
|
||
providers:
|
||
runninghub:
|
||
polling-interval: 10000 # 10秒轮询
|
||
max-polling-times: 60
|
||
|
||
spring:
|
||
datasource:
|
||
hikari:
|
||
maximum-pool-size: 30
|
||
minimum-idle: 10
|
||
|
||
server:
|
||
tomcat:
|
||
threads:
|
||
max: 300
|
||
min-spare: 50
|
||
```
|
||
|
||
**预期性能:**
|
||
- ✅ 轮询时间:~10秒/轮
|
||
- ✅ CPU使用率:<20%
|
||
- ✅ 内存占用:<2GB
|
||
- ✅ 网络带宽:~2MB/分钟
|
||
|
||
---
|
||
|
||
### 方案B:高并发配置(成熟期)
|
||
|
||
**目标:** 性能优先,支持 **500个并发任务**
|
||
|
||
```yaml
|
||
ai:
|
||
providers:
|
||
runninghub:
|
||
polling-interval: 15000 # 15秒轮询(减少频率)
|
||
max-polling-times: 40 # 最大轮询40次 = 10分钟
|
||
batch-size: 50 # 分批查询,每批50个
|
||
|
||
spring:
|
||
datasource:
|
||
hikari:
|
||
maximum-pool-size: 100
|
||
minimum-idle: 20
|
||
|
||
server:
|
||
tomcat:
|
||
threads:
|
||
max: 1000
|
||
min-spare: 100
|
||
|
||
# 异步线程池配置
|
||
task:
|
||
execution:
|
||
pool:
|
||
core-size: 50
|
||
max-size: 200
|
||
queue-capacity: 500
|
||
```
|
||
|
||
**优化代码(分批轮询):**
|
||
```java
|
||
@Scheduled(fixedDelay = 15000)
|
||
public void pollRunningHubTasks() {
|
||
List<AiTask> allTasks = aiTaskMapper.findProcessingTasksByProvider("runninghub");
|
||
|
||
// 分批处理,每批50个
|
||
int batchSize = 50;
|
||
for (int i = 0; i < allTasks.size(); i += batchSize) {
|
||
List<AiTask> batch = allTasks.subList(i, Math.min(i + batchSize, allTasks.size()));
|
||
|
||
// 并发查询这批任务
|
||
batch.parallelStream().forEach(task -> {
|
||
try {
|
||
pollSingleTask(task);
|
||
} catch (Exception e) {
|
||
log.error("轮询任务失败: {}", task.getTaskNo(), e);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
**预期性能:**
|
||
- ✅ 轮询时间:~15秒/轮(分批并发)
|
||
- ✅ CPU使用率:<50%
|
||
- ✅ 内存占用:<4GB
|
||
- ✅ 网络带宽:~10MB/分钟
|
||
|
||
---
|
||
|
||
### 方案C:极致优化(企业级)
|
||
|
||
**目标:** 支持 **2000+并发任务**
|
||
|
||
**架构升级:**
|
||
1. **Redis消息队列** 替代数据库轮询
|
||
2. **独立轮询服务** 部署多个实例
|
||
3. **负载均衡** 分散WebSocket连接
|
||
4. **限流熔断** 保护RunningHub API
|
||
|
||
```mermaid
|
||
graph LR
|
||
A[用户请求] --> B[API网关]
|
||
B --> C[业务服务集群]
|
||
C --> D[Redis队列]
|
||
D --> E1[轮询服务1]
|
||
D --> E2[轮询服务2]
|
||
D --> E3[轮询服务3]
|
||
E1 --> F[RunningHub API]
|
||
E2 --> F
|
||
E3 --> F
|
||
F --> G[MySQL主从]
|
||
C --> H[WebSocket集群]
|
||
```
|
||
|
||
---
|
||
|
||
## 📈 五、RunningHub API限流策略
|
||
|
||
### 5.1 客户端限流(防止自身被封)
|
||
|
||
```java
|
||
@Component
|
||
public class RunningHubRateLimiter {
|
||
|
||
private final RateLimiter submitLimiter = RateLimiter.create(10.0); // 每秒10个提交
|
||
private final RateLimiter queryLimiter = RateLimiter.create(50.0); // 每秒50个查询
|
||
|
||
public void beforeSubmit() {
|
||
submitLimiter.acquire(); // 阻塞直到获得许可
|
||
}
|
||
|
||
public void beforeQuery() {
|
||
queryLimiter.acquire();
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.2 熔断降级(RunningHub故障时)
|
||
|
||
```java
|
||
@Service
|
||
public class RunningHubProviderImpl implements AIProvider {
|
||
|
||
@HystrixCommand(
|
||
fallbackMethod = "submitTaskFallback",
|
||
commandProperties = {
|
||
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "10000"),
|
||
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
|
||
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")
|
||
}
|
||
)
|
||
public ProviderTaskResponse submitTask(ProviderTaskRequest request) {
|
||
// 正常提交逻辑
|
||
}
|
||
|
||
public ProviderTaskResponse submitTaskFallback(ProviderTaskRequest request) {
|
||
log.error("RunningHub服务降级,任务提交失败");
|
||
return ProviderTaskResponse.builder()
|
||
.status(TaskSubmitStatus.FAILED)
|
||
.errorMessage("RunningHub服务暂时不可用,请稍后重试")
|
||
.build();
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🧪 六、压力测试方案
|
||
|
||
### 6.1 测试工具
|
||
|
||
**JMeter测试脚本:**
|
||
```xml
|
||
<HTTPSamplerProxy>
|
||
<stringProp name="HTTPSampler.domain">localhost</stringProp>
|
||
<stringProp name="HTTPSampler.port">8081</stringProp>
|
||
<stringProp name="HTTPSampler.path">/user/ai/tasks/submit</stringProp>
|
||
<stringProp name="HTTPSampler.method">POST</stringProp>
|
||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||
<ThreadGroup>
|
||
<intProp name="ThreadGroup.num_threads">100</intProp>
|
||
<intProp name="ThreadGroup.ramp_time">10</intProp>
|
||
<intProp name="LoopController.loops">1</intProp>
|
||
</ThreadGroup>
|
||
</HTTPSamplerProxy>
|
||
```
|
||
|
||
### 6.2 测试场景
|
||
|
||
#### 场景1:渐进式压力测试
|
||
|
||
```bash
|
||
# 阶段1:10并发,持续1分钟
|
||
# 阶段2:50并发,持续5分钟
|
||
# 阶段3:100并发,持续10分钟
|
||
# 阶段4:200并发,持续10分钟
|
||
```
|
||
|
||
**观察指标:**
|
||
- API响应时间(P50、P95、P99)
|
||
- 轮询延迟
|
||
- 数据库连接池使用率
|
||
- CPU/内存使用率
|
||
- RunningHub API错误率
|
||
|
||
#### 场景2:峰值冲击测试
|
||
|
||
```bash
|
||
# 突然提交500个任务,观察系统恢复能力
|
||
ab -n 500 -c 100 -T 'application/json' \
|
||
-H "Authorization: Bearer TEST_TOKEN" \
|
||
-p task_payload.json \
|
||
http://localhost:8081/user/ai/tasks/submit
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 七、监控与告警
|
||
|
||
### 7.1 关键指标
|
||
|
||
```sql
|
||
-- 实时并发任务数
|
||
SELECT COUNT(*) as running_tasks
|
||
FROM ai_task
|
||
WHERE provider_type = 'runninghub'
|
||
AND status = 'processing';
|
||
|
||
-- 平均处理时间
|
||
SELECT AVG(TIMESTAMPDIFF(SECOND, start_time, complete_time)) as avg_seconds
|
||
FROM ai_task
|
||
WHERE provider_type = 'runninghub'
|
||
AND status = 'completed'
|
||
AND complete_time > DATE_SUB(NOW(), INTERVAL 1 HOUR);
|
||
|
||
-- 失败率
|
||
SELECT
|
||
COUNT(CASE WHEN status = 'failed' THEN 1 END) * 100.0 / COUNT(*) as failure_rate
|
||
FROM ai_task
|
||
WHERE provider_type = 'runninghub'
|
||
AND create_time > DATE_SUB(NOW(), INTERVAL 1 HOUR);
|
||
```
|
||
|
||
### 7.2 告警规则
|
||
|
||
```yaml
|
||
alerts:
|
||
- name: "RunningHub并发过高"
|
||
condition: running_tasks > 200
|
||
action: 发送邮件/短信通知
|
||
|
||
- name: "RunningHub失败率过高"
|
||
condition: failure_rate > 10%
|
||
action: 触发熔断,停止新任务提交
|
||
|
||
- name: "轮询延迟过高"
|
||
condition: polling_delay > 30s
|
||
action: 重启轮询调度器
|
||
```
|
||
|
||
---
|
||
|
||
## 💡 八、最佳实践建议
|
||
|
||
### 8.1 初期部署(100并发以内)
|
||
|
||
1. ✅ 使用**方案A配置**
|
||
2. ✅ 轮询间隔设置为 **10秒**(已调整)
|
||
3. ✅ 监控RunningHub API响应时间
|
||
4. ✅ 每日检查失败任务并分析原因
|
||
|
||
### 8.2 业务增长期(100-500并发)
|
||
|
||
1. ✅ 升级到**方案B配置**
|
||
2. ✅ 实施分批轮询优化
|
||
3. ✅ 增加数据库连接池和Tomcat线程数
|
||
4. ✅ 引入限流和熔断机制
|
||
|
||
### 8.3 大规模部署(500+并发)
|
||
|
||
1. ✅ 采用**方案C架构**
|
||
2. ✅ 独立部署轮询服务集群
|
||
3. ✅ 使用Redis消息队列
|
||
4. ✅ 实施全链路监控和自动告警
|
||
|
||
---
|
||
|
||
## 📝 九、RunningHub API费用估算
|
||
|
||
### 9.1 任务成本分析
|
||
|
||
根据 `V5__add_provider_support.sql` 中的积分配置:
|
||
|
||
| 模型类型 | 积分消耗 | 实际成本(假设1元=100积分) |
|
||
|---------|---------|---------------------------|
|
||
| 文生视频(10秒,普通) | 160 | 1.6元 |
|
||
| 文生视频(10秒,高清) | 420 | 4.2元 |
|
||
| 图生视频(10秒,普通) | 180 | 1.8元 |
|
||
| 图生视频(10秒,高清) | 480 | 4.8元 |
|
||
|
||
### 9.2 并发成本估算
|
||
|
||
**场景:100个并发任务/小时**
|
||
|
||
假设任务分布:
|
||
- 60% 文生视频(普通):60 × 1.6元 = 96元
|
||
- 20% 文生视频(高清):20 × 4.2元 = 84元
|
||
- 15% 图生视频(普通):15 × 1.8元 = 27元
|
||
- 5% 图生视频(高清):5 × 4.8元 = 24元
|
||
|
||
**总计:231元/小时** 或 **5544元/天**
|
||
|
||
**建议:**
|
||
- 对于高频用户,设置每日任务数量限制
|
||
- 引入会员等级,高级会员享受折扣
|
||
- 批量购买积分时提供优惠
|
||
|
||
---
|
||
|
||
## ✅ 十、总结与建议
|
||
|
||
### 当前配置(已优化)
|
||
|
||
```yaml
|
||
polling-interval: 10000 # ✅ 已改为10秒
|
||
max-polling-times: 60 # ✅ 已调整为60次
|
||
```
|
||
|
||
### 并发能力评估
|
||
|
||
| 并发任务数 | 系统负载 | 推荐配置 | 风险等级 |
|
||
|-----------|---------|---------|---------|
|
||
| 0-100 | 低 | 方案A | ✅ 安全 |
|
||
| 100-200 | 中 | 方案A | ⚠️ 注意 |
|
||
| 200-500 | 高 | 方案B | ⚠️ 风险 |
|
||
| 500+ | 极高 | 方案C | ❌ 需重构 |
|
||
|
||
### 下一步行动
|
||
|
||
1. **短期(1周内):**
|
||
- [ ] 部署当前配置(10秒轮询)
|
||
- [ ] 监控前100个任务的表现
|
||
- [ ] 收集RunningHub API响应时间数据
|
||
|
||
2. **中期(1个月内):**
|
||
- [ ] 进行渐进式压力测试
|
||
- [ ] 找到RunningHub API的并发阈值
|
||
- [ ] 根据实际情况调整轮询间隔
|
||
|
||
3. **长期(3个月内):**
|
||
- [ ] 如果并发超过200,实施方案B
|
||
- [ ] 引入Redis队列和独立轮询服务
|
||
- [ ] 建立完善的监控和告警体系
|
||
|
||
---
|
||
|
||
**RunningHub并发分析完成!** 🎯
|
||
|
||
当前配置已优化为10秒轮询,建议从小规模开始,逐步增加并发,实时监控系统表现。
|
||
|