Android 网络请求超时?可能与连接池和脏连接有关

在IOT开发连接Soft AP后发起网络请求时,发现一个奇怪的现象,经常有网络请求超时,一开始以为是硬件(提供Web服务)的原因,因为客户端代码看到网络请求已经出去了,但是硬件端反馈就是没收到。那问题出在哪里呢?

一、连接池与脏连接

先介绍一下基础概念。

1、为什么需要连接池

因为连接复用带来的性能收益非常大,具体对比如下:

项目 无连接池 有连接池
每次请求 建立 TCP + TLS 握手(几十~上百 ms) 直接发送请求(几 ms)
服务器负载 每次都新建 Socket、TLS 握手 复用已有连接,减少 CPU
网络流量 多次三次握手、证书传输 复用连接减少控制包
用户体验 慢、延迟高 快、流畅

我们在Android开发中一般使用的是Okhttp,Okhttp的源码中连接池的大小是5,连接的保活时间是5分钟,源码如下:

kotlin 复制代码
class ConnectionPool internal constructor(
  internal val delegate: RealConnectionPool
) {
  constructor(
    maxIdleConnections: Int,
    keepAliveDuration: Long,
    timeUnit: TimeUnit
  ) : this(RealConnectionPool(
      taskRunner = TaskRunner.INSTANCE,
      maxIdleConnections = maxIdleConnections,
      keepAliveDuration = keepAliveDuration,
      timeUnit = timeUnit
  ))
   
   //连接池默认构造
  constructor() : this(5, 5, TimeUnit.MINUTES)
...略...
}

2、什么是脏连接(stale connection)?

当客户端复用一个已经空闲的 TCP 连接时,如果 服务端已经关闭了这个连接(可能是超时、服务器主动断开、网络抖动),客户端尝试用这个连接发送请求就会失败:

kotlin 复制代码
java.io.EOFException: unexpected end of stream
java.net.SocketException: Connection reset by peer

这个连接就是所谓的 "脏连接"。因为连接池本身不知道服务端已经关闭了连接,所以当它尝试重用就会出现上面的问题。

二、遇到的问题与解决办法

当时是IOT开发,硬件侧是一个低功耗摄像头,硬件有开启一个Web服务供客户端请求,然后我发现总是有接口请求超时,我就很纳闷,硬件端抓日志说我的请求没收到???

一开始是怀疑硬件端网络请求队列的问题,因为有些页面有高并发,有些请求会不会在队列中等待时间过长就超时了? 后面放宽了超时时间,发现还是有超时现象,这就很奇怪了!!!

后面注意力才转移到脏连接上来,一开始使用了Okhtto的重试机制:

kotlin 复制代码
.retryOnConnectionFailure(true) // 保留自动重试

发现并不能解决问题,正如这个函数描述的那样:

kotlin 复制代码
/**
 * ...略...
 * Stale pooled connections. The [ConnectionPool] reuses sockets
 *   to decrease request latency, but these connections will occasionally time out.
 * ...略...
 */
fun retryOnConnectionFailure(retryOnConnectionFailure: Boolean) = apply {
  this.retryOnConnectionFailure = retryOnConnectionFailure
}

复用连接池偶尔会请求超时...

后面跟硬件工程师讨论,他说硬件端往往资源比较紧张,硬件用的这个三方网络库比较轻量,可能没有连接池这些东西或者Socket keep-alive的时间很短会及时释放。硬件工程师比较忙,也没办法继续协助我排查问题,后面查资料发现

Nginx/Tomcat 默认 Keep-Alive 超时时间可能只有 1~5 秒

这样的话使用Okhttp的连接池就没什么意义了,直接禁用了连接池解决了上面的问题:

kotlin 复制代码
.connectionPool(ConnectionPool(0, 1, TimeUnit.SECONDS))   //不复用连接

三、总结

很多看着奇奇怪怪的问题,其实很有可能是底层没有完全弄懂,要保持一定的探索欲,能解决现实问题也能让我们更加明白深层次的原理。

相关推荐
阿巴斯甜11 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker11 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952712 小时前
Andorid Google 登录接入文档
android
黄林晴13 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android