# 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秒轮询，建议从小规模开始，逐步增加并发，实时监控系统表现。

