You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Coroutines批量调用API出现HTTP 503错误的解决方案咨询

解决API并发调用HTTP 503问题的方案

你的问题核心是瞬间发起30个API请求触发了服务端限流机制(HTTP 503通常表示服务因请求过载暂时不可用)。直接加固定延迟会严重影响用户体验,以下是更高效的解决方法:

1. 用信号量(Semaphore)控制并发数

通过kotlinx.coroutines.sync.Semaphore限制同时发起的API请求数量,比如同时允许4-5个请求,既保证执行效率,又不会触发服务端限流。

修改后的代码示例:

override suspend fun getScoreboard(docId: String): ScoreboardResult {
    val scoreboardResult = ScoreboardResult()
    try {
        withContext(Dispatchers.IO) {
            val cfHandles = getCfHandles(docId = docId)
            val contests = getContests(docId = docId).split(";")
            Log.d(TAG, "contests size = ${contests.size}  ===>  $contests")
            Log.d(TAG, Thread.currentThread().toString())
            
            // 限制同时最多5个并发请求,可根据API实际限流调整
            val semaphore = Semaphore(5)
            val jobs = mutableListOf<Job>()
            // 保证结果更新线程安全
            val mutex = Mutex()
            
            for(contest in contests) {
                val options = hashMapOf("contestId" to contest, "handles" to cfHandles)
                Log.d(TAG, "Doing contest: $contest")
                
                val job = launch {
                    semaphore.acquire() // 获取请求许可,无许可则等待
                    try {
                        Log.d(TAG, Thread.currentThread().toString())
                        val contestScore = getPartiesScore(options = options)
                        if (contestScore.status == Constants.CF_API_SUCCESS_STATUS) {
                            Log.d(TAG, "Success $contest")
                            mutex.withLock {
                                scoreboardResult.updateScore(contestScore.result.rows)
                            }
                        } else {
                            throw CustomException(message = "Failed at contest: $contest")
                        }
                    } finally {
                        semaphore.release() // 释放许可,让后续请求可以执行
                    }
                }
                jobs.add(job)
            }
            
            jobs.joinAll() // 等待所有请求完成后再返回结果
        }
        Log.d(TAG, "Everything went well")
        return scoreboardResult
    } catch (exception: Exception) {
        Log.d(TAG, "Something bad happened : ${exception.message}")
        scoreboardResult.exception = exception
        return scoreboardResult
    }
}

2. 增加指数退避重试机制

针对偶尔出现的503错误,添加自动重试逻辑,用指数递增的延迟(比如100ms→200ms→400ms),既避免频繁重试触发限流,又能提高请求成功率。

封装重试函数示例:

private suspend fun fetchContestScore(options: HashMap<String, String>): PartiesScore {
    var currentDelay = 100L
    val maxRetries = 3
    
    repeat(maxRetries) { attempt ->
        try {
            return getPartiesScore(options)
        } catch (e: Exception) {
            if (attempt == maxRetries - 1) throw e // 最后一次重试失败则抛出异常
            delay(currentDelay)
            currentDelay *= 2 // 延迟翻倍
        }
    }
    throw IllegalStateException("Retry logic failed")
}

方案优势

  • 信号量控制并发:避免瞬间请求过载,让请求均匀分散,适配服务端的速率限制
  • 指数退避重试:处理临时服务过载情况,比固定延迟更高效,不会拉长整体耗时
  • 线程安全修复:原代码中lateinit var contestScore存在多协程数据冲突风险,修改后用局部变量+Mutex保证结果更新安全

内容的提问来源于stack exchange,提问作者kotlin_noob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 19:03:07