Files
1818web-hoduan/RUNNINGHUB_CONCURRENCY_ANALYSIS.md
2025-11-14 17:41:15 +08:00

15 KiB
Raw Blame History

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的并发任务数
  • 账户级别的任务队列限制

建议测试方案:

# 逐步压力测试
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. 网络带宽限制

  • 请求体大小:

    • 文生视频:~2KBprompt + 配置)
    • 图生视频:~10KB-500KB包含图片URL或Base64
  • 响应体大小:

    • 提交响应:~500B
    • 状态查询:~300B
    • 结果获取:~1KB只返回URL

预计带宽需求100并发

上传100 × 500KB = 50MB
下载轮询10次/任务100 × 10 × 1KB = 1MB
总计:~51MB一次性峰值

🔧 二、当前系统并发配置

2.1 系统参数

# 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. 轮询调度器负载

@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秒

解决方案:

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个线程

解决方案:

server:
  tomcat:
    threads:
      max: 500  # 增加最大线程数
      min-spare: 50

🚀 四、并发优化方案

方案A保守配置推荐用于初期

目标: 稳定性优先,支持 100个并发任务

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个并发任务

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

优化代码(分批轮询):

@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
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 客户端限流(防止自身被封)

@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故障时

@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测试脚本

<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渐进式压力测试

# 阶段110并发持续1分钟
# 阶段250并发持续5分钟
# 阶段3100并发持续10分钟
# 阶段4200并发持续10分钟

观察指标:

  • API响应时间P50、P95、P99
  • 轮询延迟
  • 数据库连接池使用率
  • CPU/内存使用率
  • RunningHub API错误率

场景2峰值冲击测试

# 突然提交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 关键指标

-- 实时并发任务数
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 告警规则

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元/天

建议:

  • 对于高频用户,设置每日任务数量限制
  • 引入会员等级,高级会员享受折扣
  • 批量购买积分时提供优惠

十、总结与建议

当前配置(已优化)

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