項目中因為一些歷史原因,有著大量的并發(fā)下載澎灸。在一次體驗優(yōu)化的過程中我們對AFNetworking設(shè)置了
? ? [request setTimeoutInterval:10];
認(rèn)為一個連接如果發(fā)起10s后還未開始,變認(rèn)為超時椅寺,顯示錯誤提示施籍。
然而不久后我們發(fā)現(xiàn)當(dāng)進(jìn)行大量并發(fā)小文件下載的時候,會刷出一批批的請求超時
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=請求超時。}
一開始毫無頭緒裹唆,因為網(wǎng)絡(luò)肯定是正常的誓斥,那為什么會請求超時呢。
項目中有引入FLEX许帐,借助FLEX我們看了一下超時前成功的請求的狀態(tài)
image.png
發(fā)現(xiàn)一個請求在排隊花費的時間遠(yuǎn)大于請求的時間劳坑,而且接近了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成畦,即使不是因為弱網(wǎng)造成的也會被判定為超時距芬。
那為什么會排隊呢?
查看 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個并發(fā)連接,超過4個遍會進(jìn)入排隊羡鸥。排隊導(dǎo)致后續(xù)任務(wù)超時蔑穴。
測試了http在keep alive開關(guān)下的情況
設(shè)置timeout時間1s 發(fā)起200個連接
keepalive開啟 成功100個左右
keepalive關(guān)閉 成功50個不到
猜測
成功數(shù)變化是由于keep alive導(dǎo)致連接時變化引起的
成功數(shù)變化是由于keep alive下一個connection包括了多個task?
于是解決辦法很簡單惧浴,增大HTTPMaximumConnectionsPerHost或者延長超時時間存和。但是為什么蘋果要做出這個默認(rèn)限制(默認(rèn)值還這么低)呢?
如果用戶沒有設(shè)置HTTPMaximumConnectionsPerHost的值,那么iOS系統(tǒng)的最大并發(fā)數(shù) 最大值如文檔所說,的確是4(OSX 未驗證),如果用戶設(shè)置了最大并發(fā)數(shù),則按照用戶設(shè)置的最大并發(fā)數(shù)執(zhí)行(我設(shè)置的最大20,最小為1,均可以執(zhí)行)