Ktor HttpClient(CIO)连接数超限致请求超时问题求助
问题场景代码
KtorBase2.kt
open class KtorBase2() { private val _httpClient: HttpClient val httpClient get() = _httpClient init { _httpClient = createHttpClient() } private fun createHttpClient(): HttpClient { return HttpClient(CIO) { engine { // this: CIOEngineConfig // I don't know why Request timeout occurs when I try to request beyond the maxConnectionsCount or maxConnectionsPerRoute values. maxConnectionsCount = 12 // system default=1000 endpoint { // this: EndpointConfig maxConnectionsPerRoute = 100 // system default=100 pipelineMaxSize = 20 // system default=20 keepAliveTime = 5000 // system default=5000 connectTimeout = 5000 // system default=5000 connectAttempts = 1 // system default=1 } } } } }
HomeDataAccesss.kt
suspend fun getTestHtml(ktorBase2: KtorBase2) : String { val response: HttpResponse = ktorBase2.httpClient.get("http://ap2.shinwonsoft.com/test/test.html") { } return response.body() }
HomeViewModel.kt
fun reqTestHtml(ktorBase2: KtorBase2, testCountForManyRequest: Int) { viewModelScope.launch { val str = HomeDataAccess.getTestHtml(ktorBase2) Log.d(logTag, "### reqTestHtml::html=$str, testCountForManyRequest=$testCountForManyRequest") } }
HomeFragment.kt
class HomeFragment: Fragment { private var _testCountForManyRequest: Int = 0 private val _testKtorForManyRequest: KtorBase2 = KtorBase2(URLProtocol.HTTP) ... private fun initViewEventListener() { binding.textHome.setOnClickListener { (it as TextView).text = "Home fragment: ${++_testCountForManyRequest}" _homeViewModel.reqTestHtml(_testKtorForManyRequest, _testCountForManyRequest) } } }
控制台日志
19:05:33.635 HE_HomeViewModel D ### reqTestHtml::html=test..., testCountForManyRequest=1 19:06:52.385 HE_HomeViewModel D ### reqTestHtml::html=test..., testCountForManyRequest=2 19:07:28.888 HE_HomeViewModel D ### reqTestHtml::html=test..., testCountForManyRequest=3 19:07:29.653 HE_HomeViewModel D ### reqTestHtml::html=test..., testCountForManyRequest=4 19:07:30.231 HE_HomeViewModel D ### reqTestHtml::html=test..., testCountForManyRequest=5 19:07:30.726 HE_HomeViewModel D ### reqTestHtml::html=test..., testCountForManyRequest=6 19:07:31.266 HE_HomeViewModel D ### reqTestHtml::html=test..., testCountForManyRequest=7 19:07:32.122 HE_HomeViewModel D ### reqTestHtml::html=test..., testCountForManyRequest=8 19:07:33.303 HE_HomeViewModel D ### reqTestHtml::html=test..., testCountForManyRequest=9 19:11:11.393 HE_HomeViewModel D ### reqTestHtml::html=test..., testCountForManyRequest=10 19:11:30.733 HE_HomeViewModel D ### reqTestHtml::html=test..., testCountForManyRequest=11 19:11:36.508 HE_HomeViewModel D ### reqTestHtml::html=test..., testCountForManyRequest=12 19:11:48.195 android.ha...mpl.ranchu D threadLoop: entering standby, frames: 2233440 --------- beginning of crash 19:11:50.138 StatusBarIconController D ignoring old pipeline callbacks, because the new mobile icons are enabled 19:11:50.167 AndroidRuntime E FATAL EXCEPTION: main Process: kr.co.heavyequipment, PID: 4914 io.ktor.client.network.sockets.ConnectTimeoutException: Connect timeout has expired [url=http://ap2.shinwonsoft.com/test/test.html, connect_timeout=unknown ms] at io.ktor.client.plugins.HttpTimeoutKt.ConnectTimeoutException(HttpTimeout.kt:213) at io.ktor.client.plugins.HttpTimeoutKt.ConnectTimeoutException$default(HttpTimeout.kt:210) at io.ktor.client.engine.cio.Endpoint.getTimeoutException(Endpoint.kt:268) at io.ktor.client.engine.cio.Endpoint.connect(Endpoint.kt:257) at io.ktor.client.engine.cio.Endpoint.access$connect(Endpoint.kt:25) at io.ktor.client.engine.cio.Endpoint$connect$1.invokeSuspend(Unknown Source:15) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.internal.ScopeCoroutine.afterResume(Scopes.kt:32) at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115) at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:103) at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684) Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@5f7479, Dispatchers.Main.immediate] 19:11:50.173 DropBoxManagerService I add tag=data_app_crash isTagEnabled=true flags=0x2 19:11:50.175 ActivityTaskManager W Force finishing activity kr.co.he/.ui.main.MainActivity
问题分析与解决方案
1. 超时原因
CIO的连接池机制是:当请求数超过maxConnectionsCount或maxConnectionsPerRoute时,新请求会进入等待队列(由pipelineMaxSize控制队列长度)。如果队列占满,或者请求等待可用连接的时间超过connectTimeout设定值,就会触发ConnectTimeoutException。
你的代码仅配置了连接超时,未设置请求等待超时,短时间内大量请求会快速占满连接池和队列,后续请求因等待时间过长直接触发超时。
2. 修复方案
(1)添加请求超时配置
在HttpClient中安装HttpTimeout插件,明确设置各类超时参数:
return HttpClient(CIO) { install(HttpTimeout) { requestTimeoutMillis = 10000 // 整个请求的总超时 connectTimeoutMillis = 5000 // 连接建立超时 socketTimeoutMillis = 5000 // 读写数据超时 } engine { maxConnectionsCount = 12 endpoint { maxConnectionsPerRoute = 100 pipelineMaxSize = 20 keepAliveTime = 5000 connectTimeout = 5000 connectAttempts = 1 } } }
这样请求等待连接超过设定时间时,会抛出明确的超时异常,而非无限阻塞。
(2)合理调整连接池参数
- 根据业务场景调大
maxConnectionsCount和maxConnectionsPerRoute,但不要过度设置; - 调大
pipelineMaxSize可增加等待队列长度,避免请求直接被拒绝,但队列过长会导致请求等待时间变长。
(3)确保连接正常释放
Ktor会自动管理连接复用,但如果请求出现未处理的异常或阻塞逻辑,可能导致连接无法及时回收到连接池。确保请求逻辑无阻塞,异常被正确捕获处理。
3. 无限调大连接数的影响
会给Android终端带来明显负载:
- 每个TCP连接会占用内存、文件描述符等系统资源,Android对单APP的文件描述符数量有限制,过多连接会触发OOM或崩溃;
- 大量并发请求会增加CPU和网络模块负载,导致APP卡顿、耗电增加,甚至被系统判定为异常进程杀死;
- 服务器端也会限制并发连接数,过多请求可能被拒绝或限流。
建议根据业务需求设置合理值,移动端一般maxConnectionsCount在20-50之间即可应对大部分场景。
内容的提问来源于stack exchange,提问作者newmanjoo
相关产品推荐
相关产品推荐

