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

三、总结

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

相关推荐
程序员JerrySUN41 分钟前
Jetson边缘嵌入式实战课程第五讲:Jetson Secure Boot - 安全启动
android·linux·服务器·人工智能·安全·unity·游戏引擎
zh_xuan1 小时前
Android JNI 动态注册:获取系统内存页大小
android·cmake·jni·ndk·动态注册·内存页大小
CocoaKier1 小时前
X未提前通知,突然停用twitter授权登录域名,大量X三方登录异常!
android·ios
0pen12 小时前
android-sqlite3:从官方 SQLite 源码自动构建 Android 可用的 sqlite3
android·数据库·sqlite
测试开发-学习笔记2 小时前
adb命令
android·adb
plainGeekDev2 小时前
Android四大组件面试题,看完这篇就够了
android·面试·kotlin
私人珍藏库2 小时前
【Android】Todesk手机远控手机、电脑,无会员无广告!!
android·学习·智能手机·app·工具·软件·多功能
JohnnyDeng943 小时前
Android 动画体系:属性动画与 Compose 动画对比
android
独隅3 小时前
MySQL主从延迟根因诊断法:全面详解指南
android·mysql·adb
私人珍藏库3 小时前
【Android】图片工具箱-免费开源图片处理软件
android·人工智能·app·工具·软件·多功能