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))   //不复用连接

三、总结

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

相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone3 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui