昨天被分到一个Bug,公司某产品在IE下偶尔会随机出现请求挂起,等待30秒后弹出超时错误(错误状态码:12152)的问题,而在FireFox或Chrome则从没有这样的问题。据说这个问题已经困扰他们四年多了,一直混着。直到最近在某个大客户的新环境中频频出现,才不得不专门找人(
)解决。接到这个Bug,感觉就是某个经典的IE Repost问题,之前一直没有机会详细了解,借这个机会翻了一些资料,在这里总结一下。
产生原因
现在的HTTP连接,特别是HTTPS连接,为了提高性能,几乎全部采用Keep-Live模式。也就是连接建立起来后会存活一段时间(数秒到数十秒不等),这段时间内的请求会重用这个连接。根据HTTP 1.1 (RFC 2616)标准,连接路径上的代理服务器,负载均衡服务器,应用服务器等节点都可以随时中止这个连接。一般情况下如果网速较快,连接被中止时浏览器会立刻知道,下一个请求就建立新连接,不会出现明显问题。但在网速不佳的环境中,会出现一种边界场景:浏览器发出了一个请求,但在请求还没到服务器前,服务器端认为连接超时,中止了连接。这时浏览器会立刻得到一个请求超时错误,根据RFC 2616标准,浏览器这时应该无须用户干预,自动重新建立新的连接并且重发之前的请求。各大浏览器都遵循了这个规范,因此在FireFox或Chrome下,用户根本不会意识到有Keep-Live连接超时这回事。
引用
原文如下:
A client, server, or proxy MAY close the transport connection at any time. For example, a client might have started to send a new request at the same time that the server has decided to close the "idle" connection. From the server's point of view, the connection is being closed while it was idle, but from the client's point of view, a request is in progress.
This means that clients, servers, and proxies MUST be able to recover from asynchronous close events. Client software SHOULD reopen the transport connection and retransmit the aborted sequence of requests without user interaction so long as the request sequence is idempotent (see section 9.1.2).
但,IE的XMLHttpRequest实现(某COM组件)在重发请求时有个低级错误:只会重发header部分,而忘了重发原来请求的内容部分!并且,在header中,仍然包含了原来请求中的content length (内容大小)数据。服务器在接收到这个请求header后,就开始望眼欲穿地等待指定大小的请求内容,而IE根本不会继续发送任何内容。于是,一段时间后,用户收到了一个真正的连接超时错误。
这个问题,根据微软官方文档,确认在IE6-9中都存在,IE10和后续版本到底如何(以及IE10的兼容模式下到底如何),我暂时还没找到明确文档。
如果你已经开始惊叹IE的弱智,别急,这只是开胃菜,更无厘头的在后面。
微软在收到用户投诉后,发了一个hotfix ( http://support.microsoft.com/kb/895954/en-us ),题目翻译过来大概是《当你使用IE或其他程序重发post请求时,只有请求头会被发送》,全文没有任何一处提到12152错误码和上面的错误现象。最开始也不知道是哪位天才把上述问题和这个hotfix联系起来的。
在IE6下,用户需要下载安装这个hotfix。而IE7-9,则已经内置了这个hotfix。但是————这个hotfix默认是禁用的,用户需要手动在注册表中添加一个名为 FEATURE_SKIP_POST_RETRY_ON_INTERNETWRITEFILE_KB895954 (
!!!!) 的项目来开启它。具体操作可以参考 http://support.microsoft.com/kb/895954/zh-cn 的《如何启用此修补程序》一节,复制如下:
引用
如何启用此修补程序
警告如果使用注册表编辑器或其他方法错误地修改了注册表,可能会出现严重问题。这些问题可能要求您重新安装操作系统。Microsoft 不能保证这些问题能够得到解决。修改注册表的风险由您自己承担。
若要添加此注册表值,请执行以下步骤:
1. 单击开始,单击运行,键入regedit,然后单击确定
2. 找到并单击以下注册表子项之一。管理客户端计算机的相应子项使用所指定的策略。
* HKEY_CURRENT_USER\Software\Policies\Microsoft\Internet Explorer\Main\FeatureControl
* HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl
* HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Internet Explorer\Main\FeatureControl
* HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Main\FeatureControl
3. 在编辑菜单上,单击新建,然后单击项。
键入FEATURE_SKIP_POST_RETRY_ON_INTERNETWRITEFILE_KB895954作为子项的名称,然后按 enter 键。
4. 单击FEATURE_SKIP_POST_RETRY_ON_INTERNETWRITEFILE_KB895954子项。
5. 在编辑菜单上,指向新建,然后单击DWORD 值。
6. 为 dword 值名称中,键入* ,然后按 enter 键。
7. 用鼠标右键单击*,然后单击修改。
8. 在数值数据框中,键入00000001,然后单击确定。
9. 退出注册表编辑器。
网上很多人摸不着头脑,这么低级且恶劣的问题,明明出来一个hotfix,后期都直接内置了,干嘛不默认打开,还搞得这么复杂。其实,微软的文档里没有说他们是如何修复这个错误的,一旦你真的开启了hotfix,就豁然开朗了。正如这个古怪的注册表项目名称所说的,微软“修复”这个错误的做法是:Skip Post Retry!!!! 没错,他们不是把重发请求的问题改好,而是直接不重发了!!!!所以,你开启fix后,会偶尔直接得到一个12152错误,而不用等30秒了,问题解决,OH YEAH。
解决方案探讨
网上有一些讨论说可以通过在相应的请求头中加入Connection:close通知浏览器每次都关闭连接解决此问题,或者通过在服务器中提高Keep-Live的timeout时间来缓解问题。但一些资料说明,这类方案有一下问题:
1. 某些服务器并不理会请求头中关于连接的设定,程序员需要手工添加一些关闭连接的代码。我发现多数直接加入Connection:close的成功案例都是PHP的,估计跟PHP直接架在Apache上有关。
2. 如果你选择提高timeout,你需要把从用户浏览器到你服务器的物理连接路径上所有忽略请求头连接设置的节点都手工设置好。(对我们公司的实际场景来说几乎不可能)
3. 每次重新创建连接的性能损耗颇大(有人说很大,有人说没感觉,具体多少我没试过,因为上面1,2已经把我踢出局了)
微软的意图很明显,要么你可以继续默认关闭hotfix,IE会非常配合地给用户一个无可挑剔的服务器超时假象,然后你可以跟客户说,你们的防火墙不稳定,自己检讨一下吧。 要么你可以让客户开启hotfix,然后自己在应用中实现一个重试机制,我这两天就在干这件事。
最后一件恶心的事:这个Keep-Live连接timeout和普通timeout都使用12152错误码,拜托IE你都出hotfix了,意图这么明显是让程序员自己解决,你至少也弄个标志让我分辨出哪个是需要在应用中重发的timeout吧。目前我采用的方案是,在请求前记录时间,如果出现12152错误且等待时间小于5秒,则认为是Keep-Live连接超时,由应用主动重发请求。
也许最好的方案是,说服客户不要用IE。
==================================
2014-01-06 补充
1. 在压力测试中发现,同样的问题并不总是返回12152错误码(暂不确定是否跟IE版本有关),有时返回12030错误码。据网上讨论,可能的错误码应为:
switch(status){
case 12029:
case 12030:
case 12031:
case 12152:
case 12159:
//检查请求等待时间,若小于5秒则重发请求并break
//否则fall through到default
default:
originalOnError.apply(this, arguments) //执行原本的onError
}
2. 有人指出把发出请求的代码异步执行(放到一个延时为0的setTimeout中)能消除此问题 ( http://stackoverflow.com/questions/8130446/fixing-the-ie-12030-repeatability-bug-for-ajax-calls ),原因未知,待测试。
分享到:
相关推荐
主要介绍了为jquery的ajax请求添加超时timeout时间的操作方法,文中通过一段简单的代码给大家介绍jquery ajax超时设置方法,感兴趣的朋友跟随脚本之家小编一起看看吧
Ajax请求session超时处理流程 java服务器端处理: SessionValidateFilter中修改: if (ServerInfo.isAjax(request)) { request.setAttribute("statusCode", 301); request.setAttribute("message", "Session ...
完美解决ie9 ajax 请求失败问题,解决ie9 跨域请求失败问题,欢迎下载,亲测有效。jquery.XDomainRequest.js
Android Webview虽然提供了页面加载及资源请求的钩子,但是对于h5的ajax请求并没有提供干涉的接口,这意味着我们不能在webview中干涉javascript发起的http请求,而有时候我们确实需要能够截获ajax请求并实现一些功能...
今天在使用 ajax 向后台请求数据时出现错误,提示状态码为 0 ,后台采用的是 spring mvc 架构。 状态码为0是什么意思呢?查找了下,原来它意味着 (未初始化)即没有调用到send()方法,我原来代码如下 : $.ajax...
Js 拦截全局ajax请求
以下图为例,页面中通过一个Load链接以Ajax请求的方式加载数据(左)。当用户点击该链接之后,Ajax请求开始,GIF图片显示“Loading“状态,同时当前页面被“罩住”防止用户继续点击Load按钮(中);Ajax请求完成被...
chrome扩展插件获取ajax请求记录
ajax请求报parsererror错误是很宽泛的概念,很多情况下都报这个错, 在很多时候,即使ajax提交、返回都正常 XMLHttpRequest.status=200 (正常响应) XMLHttpRequest.readyState=4 (正常接收) ajax也会提示一个parse...
现在Ajax在Web项目中应用广泛,几乎可以说无处不在,这就带来另外一个问题:当Ajax请求遇到Session超时,应该怎么办? 显而易见,传统的页面跳转在此已经不适用,因为Ajax请求是XMLHTTPRequest对象发起的而不
springmvc接收ajax请求注意事项
jquery中ajax方法有个属性async用于控制同步和异步,默认是true,即ajax请求默认是异步请求,有时项目中会用到AJAX同步。这个同步的意思是当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面出现
使用Ajax get请求数据的小例子,使用$.each()循环显示到表格,里面有接口,大家可以使用,欢迎指导学习
2023最新ajax请求第三方接口天气预报案例.docx2023最新ajax请求第三方接口天气预报案例.docx2023最新ajax请求第三方接口天气预报案例.docx2023最新ajax请求第三方接口天气预报案例.docx2023最新ajax请求第三方接口...
主要介绍了Ajax请求在数据量大的时候出现超时的解决方法,需要的朋友可以参考下
简单页面访问后台接口的一个样例,用于调试后台接口是否支持跨域,是否支持ajax请求,是否又正确的值返回前端,在postman正确的情况下浏览器不一定能成功,此时就需要用ajax模拟调用检查
打包好的Ajax代码,实现了对象化,使用时直接调用就可以了,调用时需要重构三个方法,如下: function onerror() //错误处理方法 { alert("error"); } function getInfo() //发送请求方法,包括请求方法和请求...
HTML使用极简的方式通过ajax请求实现前后端交互。文中不使用form表,为了方便ajax拼接其他参数。
一个完整的jquery+ajax传送请求的实例