如何通过jQuery Ajax加快大数据提交时的HTTP响应接收?
问题: 使用 python webapp2 和 jQuery Ajax 时,接收大文本数据的响应非常慢(1.7MB 的数据往返需要超过 10 分钟)。
提问: 这是什么原因?怎么改善呢?有没有什么有效的方法可以把大文本分成小块,避免“浏览器卡死”的问题?
背景: 我正在学习用 python 做网页编程,使用的是 webapp2 和 Google App Engine。我想用 jQuery Ajax 创建一个“你输入什么就能看到什么”的编辑区,类似于 stackoverflow 的帖子编辑器:wmd-input 和 wmd-preview,它提供实时预览功能。(它会不断显示“草稿已保存”给短文本。另一个例子是 Google Docs 的实时编辑功能。)
我的例子是这样的: 一个文本变化的 jQuery 插件会在每次输入框内容变化时触发 Ajax 请求 ---> Python 后端接收文本并在其上添加一些消息 ---> 然后把文本和消息一起发送回去 ---> jQuery 使用服务器的响应来更新预览文本框(实际上,发送完整的文本内容只是为了测试)。
我的前端代码:
<script type="text/javascript">
function simpleajax() {
$.ajax({
type: 'POST'
,url: '/simpleajax'
,dataType: 'json'
,data:{'esid':'#ajaxin','msgin':$('#ajaxin').val()}
,cache:false
,async:true
,success:function (resp){$('#ajaxout').text(resp.msgout);}
,error:function (jqXHR, textStatus, errorThrown){
{$('#ajaxout').text("Ajax Error:"+textStatus+","+errorThrown)}}
});
}
$(document).ready(function(){
$('#ajaxin').bind('textchange', function () {
$('#ajaxstatus').html('<strong color="blue">Typing ...</strong>');
simpleajax();
});
});
</script>
我的后端代码:
class simpleajax(BaseReqHandler):
def get(self):
content={'pagealert':'simpleAjax get response'}
self.render_to_response('simpleAjax.html',**content)
def post(self):
esid=self.POST['esid']
msgin=self.POST['msgin']
msgout="Server noticed element " + esid + " value changed" + " and saved following message: " + msgin
content={"msgout":msgout}
self.writeout(content)
测试案例和症状: 本地服务器 + 纯文本数据
把小于 500KB 的纯文本复制粘贴到输入区域:运行得很好。 但是,1.7MB 的文本让浏览器忙了超过 10 分钟,完全没有反应。
比较一下:我把同样的文本粘贴到 stackoverflow 的帖子编辑器,预览立刻就出现了!这次我没有注意到“草稿已保存”的提示。而且这里有一些 JavaScript 代码在判断文本长度。嗯,可能没有涉及到服务器的通信。但这只是个变通办法,并不是解决我问题的办法。(Google Docs 的自动保存功能一定使用了某种技术来解决这个问题!)
Firebug xhr 监控结果:
#Request Headers:
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Content-Length: 2075974
Referer: http://localhost:8080/ajax
Cookie: __utma=111872281.1883490050.1319630129.1319630129.1319637523.2; __utmz=111872281.1319630129.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)
Pragma: no-cache
Cache-Control: no-cache
#Response Headers:
Server: Development/1.0
Date: Fri, 04 Nov 2011 03:29:05 GMT
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: 1790407
#Firebug Timeline:
TimePoints TimeElapsed Actions
0 1ms DNS Lookup
+1ms 1ms Connecting
+2ms 82ms Sending
+84ms 1.03s Waiting
+1.11s 14m22s Receiving
+14m23.11s Done
有趣的事情:
- jQuery Ajax 向服务器发送了 2MB 的数据,而不是 1.7MB 的纯数据。真是个大开销!这可能是因为 Content-Type: application/x-www-form-urlencoded 吗?
- 服务器响应需要 1.03 秒,而 jQuery 接收响应却花了 14 分钟!!!
这到底是怎么回事?任何帮助都非常感谢!我希望在 Ajax 请求后让服务器“推送”很多东西到客户端,但这个问题让这一切变得不可能。
1 个回答
可以考虑使用HTML5的WebSockets和Worker API的组合,这样可以在不影响用户界面性能的情况下,异步地从服务器发送和接收数据。
http://dev.w3.org/html5/websockets/
http://net.tutsplus.com/tutorials/javascript-ajax/start-using-html5-websockets-today/(这个教程假设服务器端使用PHP技术)
http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html
另一种选择
a) 在 mousedown
和 keyup
事件时
- record the cursor position in the text-box - store it in C1.
b) 在 textchange
事件时
> record the cursor position - store it in C2.
> send only the content between C1 and C2 to your server. Also send the values C1 and C2 to the server, i.e your AJAX payload will look something like:
{ c1: C1, c2: C2, data: <text in the text-box from C1 to C2> }
你需要检查 c1 > c2
,并适当地获取子字符串,反之亦然。
这样,每次只会把“变化”的部分发送到服务器,而不是整个文本。不过,如果你复制粘贴了5MB的内容,这样做就没有任何改善。但对于单个字符的变化(比如打字、粘贴小段落等),这种方法应该效果不错。