iOS并发网络请求排队造成下载超时错误

96
称一称
0.2 2017.12.18 15:31* 字数 446

项目中因为一些历史原因,有着大量的并发下载。在一次体验优化的过程中我们对AFNetworking设置了

    [request setTimeoutInterval:10];

认为一个连接如果发起10s后还未开始,变认为超时,显示错误提示。

然而不久后我们发现当进行大量并发小文件下载的时候,会刷出一批批的请求超时

Error Domain=NSURLErrorDomain Code=-1001 "请求超时。" UserInfo={NSUnderlyingError=0x1c0450230 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=xxxxxx.zip, NSErrorFailingURLKey=https://xxxxxx.zip, _kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-2102, NSLocalizedDescription=请求超时。}

一开始毫无头绪,因为网络肯定是正常的,那为什么会请求超时呢。

项目中有引入FLEX,借助FLEX我们看了一下超时前成功的请求的状态


image.png

发现一个请求在排队花费的时间远大于请求的时间,而且接近了10s

看来原因就在……排队。

看timeoutInterval的描述

/*! 
    @abstract Sets the timeout interval of the receiver.
    @discussion The timeout interval specifies the limit on the idle
    interval allotted to a request in the process of loading. The "idle
    interval" is defined as the period of time that has passed since the
    last instance of load activity occurred for a request that is in the
    process of loading. Hence, when an instance of load activity occurs
    (e.g. bytes are received from the network for a request), the idle
    interval for a request is reset to 0. If the idle interval ever
    becomes greater than or equal to the timeout interval, the request
    is considered to have timed out. This timeout interval is measured
    in seconds.
*/

如果一个请求排队时间超过了10s,即使不是因为弱网造成的也会被判定为超时。

那为什么会排队呢?

查看 NSURLSessionConfiguration 中的HTTPMaximumConnectionsPerHost属性描述可以看到:

The maximum number of simultaneous connections to make to a given host.
This property determines the maximum number of simultaneous connections made to each host by tasks within sessions based on this configuration.
This limit is per session, so if you use multiple sessions, your app as a whole may exceed this limit. Additionally, depending on your connection to the Internet, a session may use a lower limit than the one you specify.
The default value is 6 in macOS, or 4 in iOS.

可以看到在一个urlsession中对同一个host只允许4个并发连接,超过4个遍会进入排队。排队导致后续任务超时。

测试了http在keep alive开关下的情况
设置timeout时间1s 发起200个连接
keepalive开启 成功100个左右
keepalive关闭 成功50个不到

猜测

  • 成功数变化是由于keep alive导致连接时变化引起的
  • 成功数变化是由于keep alive下一个connection包括了多个task?

于是解决办法很简单,增大HTTPMaximumConnectionsPerHost或者延长超时时间。但是为什么苹果要做出这个默认限制(默认值还这么低)呢?

iOS