`
kidneyball
  • 浏览: 328966 次
  • 性别: Icon_minigender_1
  • 来自: 南太平洋
社区版块
存档分类
最新评论

IE下Ajax请求偶发12152连接超时错误浅析

 
阅读更多
昨天被分到一个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 ),原因未知,待测试。
2
0
分享到:
评论
2 楼 leaow567 2014-01-02  
膜拜一下too
试问,这些资料信息怎么找到的
1 楼 mfkvfn 2013-12-20  
膜拜一下

相关推荐

    为jquery的ajax请求添加超时timeout时间的操作方法

    当请求超时时,我们通常可以选择放弃当前请求,避免无谓的等待;也可以根据应用的具体需求,决定是否立即重试或是延时后再试。务必注意,合理设置超时时间既可以防止网络较慢时的无响应,也能避免对服务器的过度请求...

    Ajax请求session超时处理流程

    Ajax请求session超时处理流程 java服务器端处理: SessionValidateFilter中修改: if (ServerInfo.isAjax(request)) { request.setAttribute("statusCode", 301); request.setAttribute("message", "Session ...

    ie9 ajax请求失败解决方案

    完美解决ie9 ajax 请求失败问题,解决ie9 跨域请求失败问题,欢迎下载,亲测有效。jquery.XDomainRequest.js

    Js拦截全局ajax请求

    在JavaScript开发中,有时我们需要对全局的Ajax请求进行拦截,以便进行统一的处理,比如添加统一的错误处理、数据格式化、性能监控等。这种需求通常可以通过“Ajax Hook”技术来实现。Ajax Hook允许我们捕获并修改...

    实例详解Android Webview拦截ajax请求

    Android Webview虽然提供了页面加载及资源请求的钩子,但是对于h5的ajax请求并没有提供干涉的接口,这意味着我们不能在webview中干涉javascript发起的http请求,而有时候我们确实需要能够截获ajax请求并实现一些功能...

    快速解决ajax请求出错状态码为0的问题

    今天在使用 ajax 向后台请求数据时出现错误,提示状态码为 0 ,后台采用的是 spring mvc 架构。 状态码为0是什么意思呢?查找了下,原来它意味着 (未初始化)即没有调用到send()方法,我原来代码如下 : $.ajax...

    chrome扩展插件获取ajax请求记录

    在IT领域,尤其是在Web开发中,Ajax(Asynchronous JavaScript and XML)是一种常用的技术,它使得网页可以在不重新加载整个页面的情况下与服务器交换数据并更新部分网页内容。这极大地提升了用户体验,因为用户不再...

    AJAX 请求完成时执行函数。Ajax 事件。

    AJAX请求通常包括以下几个步骤:创建XMLHttpRequest对象、打开连接、发送请求和接收响应。当请求完成时,我们需要执行一些后续操作,这通常通过设置回调函数来实现。 2. **XMLHttpRequest对象** XMLHttpRequest...

    Ajax请求在数据量大的时候出现超时的解决方法

    在开发Web应用时,我们经常会遇到使用Ajax进行数据交互的情况,特别是当数据量非常大时,可能会遇到Ajax请求超时的问题。这个问题不仅影响用户体验,也可能导致应用性能下降。本篇文章将详细探讨这个问题,并提供几...

    解决js ajax同步请求造成浏览器假死的问题

    在正常情况下,我们推荐使用异步请求,因为它们不会阻塞用户界面,从而允许用户继续与页面上的其他元素交互。然而,在某些特定的场景中,开发者可能会选择同步请求。本文将探讨由同步AJAX请求造成的浏览器假死现象,...

    SpringBoot+SpringSecurity处理Ajax登录请求问题(推荐)

    SpringBoot+SpringSecurity处理Ajax登录请求问题 SpringBoot+SpringSecurity处理Ajax登录请求问题是SpringBoot开发中的一個常见问题,本文将详细介绍如何使用SpringBoot+SpringSecurity处理Ajax登录请求问题。 ...

    IE7 Ajax跨域问题

    标题 "IE7 Ajax跨域问题" 涉及到的是在使用Ajax技术在Internet Explorer 7(IE7)浏览器中处理跨域请求时遇到的挑战。在Web开发中,Ajax(Asynchronous JavaScript and XML)是一种在不刷新整个页面的情况下与服务器...

    如何在Thymeleaf中实现ajax请求url的可靠构造

    例如,如果你的Ajax请求在`/users`路径下,你想请求服务器的`/api/user`接口,可以这样写: ```html $.ajax({ url: @{/api/user}, type: 'GET', success: function(response) { // 处理响应数据 }, error: ...

    完美解决ajax跨域请求下parsererror的错误

    ajax请求报parsererror错误是很宽泛的概念,很多情况下都报这个错, 在很多时候,即使ajax提交、返回都正常 XMLHttpRequest.status=200 (正常响应) XMLHttpRequest.readyState=4 (正常接收) ajax也会提示一个parse...

    解决Ajax 发送多个请求引发的并发问题

    使用jQuery、axios或fetch等库发送Ajax请求,可以通过设置超时、错误处理、缓存策略等进一步优化并发请求的处理。 四、示例代码 这里提供一个使用jQuery的Ajax请求队列示例: ```javascript var requestQueue = ...

    fileUpload(兼容IE的Ajax上传图片)

    7. **错误处理**:添加适当的错误处理机制,如超时、网络中断或文件格式不正确等,以提供友好的用户体验。 在提供的压缩包文件名"wet-test"中,可能包含了实现上述功能的测试代码或示例。这些文件可能包括HTML页面...

    一个完整的jquery+ajax传送请求的实例

    而AJAX(Asynchronous JavaScript and XML)则是一种在不刷新整个页面的情况下与服务器交换数据并更新部分网页的技术。通过AJAX,我们可以实现页面的无刷新加载,提高用户体验。 在这个实例中,我们将使用jQuery的$...

    HTML使用极简的方式通过ajax请求实现前后端交互代码实现

    在上述代码中,我们定义了AJAX请求的各个关键属性:url指定服务器接口,type定义请求方法,data是待发送的数据,dataType指明期望的响应格式。success和error回调分别处理成功和失败的情况。 然而,为了更简洁,...

    Ajax异步请求的验证

    console.error('请求超时'); }; xhr.onabort = function() { console.error('请求被中断'); }; ``` 四、使用jQuery简化Ajax jQuery库提供了更简洁的Ajax方法,如`$.ajax()`、`$.get()`、`$.post()`等。以下是一...

    完美解决ajax访问遇到Session失效的问题

    现在Ajax在Web项目中应用广泛,几乎可以说无处不在,这就带来另外一个问题:当Ajax请求遇到Session超时,应该怎么办? 显而易见,传统的页面跳转在此已经不适用,因为Ajax请求是XMLHTTPRequest对象发起的而不

Global site tag (gtag.js) - Google Analytics