关于“服务器推”技术与反向Ajax的一点笔记 博客分类: 读书笔记 html5ajaxjavascriptcometweb
什么是Ajax? 异步的JavaScript和XML(Asynchronous JavaScript and XML,Ajax)。浏览器允许JavaScript脚本向幕后的网站发送一个HTTP请求而又无需重新加载页面。
synchronous adj 同时发生的;同步的 【记】syn=same,chron时间,ous-同时间的-同步的
|
首先,本质上这个功能主要由浏览器完成,浏览器提供这个功能给JavaScript脚本来调用,然后浏览器根据规则来回调脚本去处理响应。所以Ajax中其实浏览器是最大苦力。JavaScript也可以换成其他脚本,另外交换数据不一定是XML格式的,所以XML也是可有可无的。所以Ajax中除了A(Asynchronous)以外,其他几个字母都是浮云。
那么浏览器是如何封装异步调用功能给JavaScript调用的呢?答案是浏览器将这个功能封装在一个JavaScript对象window.XMLHttpRequest里面,这个对象像一个代理一样,为JavaScript调用提供接口。
XMLHttpRequest提供了一些方法以及属性如下:
- open(method,url,async)方法 : 调用的参数配置
- send([string])方法:将请求发送到服务器
- onreadystatechange方法:是由浏览器回调的方法,由开发人员编写
- -- readyState :
- -- status :HTTP状态码
上文一直强调核心功能是浏览器提供的,一直没有说浏览器到底做了什么?看看上面XMLHttpRequest提供的方法,再想想TCP/IP原理,你就会明白,其实浏览器只是封装了一个Socket调用而已。XMLHttpRequest作为封装这个Socket的Proxy。
Socket一般的工作流程是怎样的?
- 1. 创建Socket对象
- 2. 建立连接
- 3. 发送数据
- 4. 接受数据
- 5. 处理接受到的数据
- 6. 关闭连接
那么Ajax的工作流程呢?
- 1. 创建 XMLHttpRequest 对象
- 2. 设置连接的相关参数open(method,url,async)
- 3. 发送数据send([string])
- 4. 处理接受到的数据onreadystatechange
对比Socket和Ajax的工作流程,你会发现经过浏览器封装后的Socket调用更加简单,你不需要主动建立连接和关闭连接,你只需要设置好相关的参数,同时写好如何处理接受到的数据的方法,那么关于Socket以及HTTP协议的细节问题就交给浏览器去做吧。
下面是一点Ajax调用的基本代码示例:
var xmlhttp=new XMLHttpRequest(); // xmlhttp.readyState //
xmlhttp.status |
上面的代码唯一有点饶人的就是onreadystatechange这个回调函数的设置罢了, 总体来说还是非常清晰易懂的。
另外要特别强调的一点,Ajax发送的内容仍然是基于HTTP协议的请求,理论上Ajax发送一个请求和你在地址栏里面输入请求是一模一样的。服务器应该根本就不知道什么是Ajax,服务器只处理HTTP请求。
在说什么是Reverse Ajax之前,先谈谈为什么要Reverse Ajax。
因为HTTP协议是无状态的,也即请求完数据后就关闭了连接。如果服务器有新的数据,浏览器是无法知道的,必须主动去查询才能知道。
同理,因为Ajax是基于HTTP的,所以Ajax请求在缺省情况下也是无状态的,且只能从客户端向服务器端发出请求。你可以通过使用技术模拟服务器端和客户端之间的响应式通信来绕过这一限制。
因此,为了尽快地获得服务器端事件,我们只能使用粗暴的HTTP轮询来完成任务。轮询的间隔(两次请求相隔的时间)必须尽可能地小。但有这样的一个缺点存在:如果间隔减小的话,客户端浏览器就会发出更多的请求,这些请求中的许多都不会返回任何有用的数据,而这将会白白地浪费掉带宽和处理资源。
用JavaScript实现的轮询的优点和缺点:
1. 优点:很容易实现,不需要任何服务器端的特定功能,且在所有的浏览器上都能工作。
2. 缺点:这种方法很少被用到,因为它是完全不具伸缩性的。试想一下,在100个客户端每个都发出2秒钟的轮询请求的情况下,所损失的带宽和资源数量,在这种情况下30%的请求没有返回数据。
那在现有的条件下,可不可以不要这么粗暴呢?答案当然是:“Yes!”
有一个叫捎带轮询(piggyback polling)的实现方式是一种比轮询更加聪明的做法,因为它会删除掉所有非必需的请求(没有返回数据的那些)。不存在时间间隔,客户端在需要的时候向服务器端发送请求。不同之处在于响应的那部分上,响应被分成两个部分:对请求数据的响应和对服务器事件的响应,如果任何一部分有发生的话,在实现piggyback技术时,通常针对服务器端的所有Ajax请求可能会返回一个混合的响应。
同样,这种方法也有着一些优点和缺点:
1. 优点:没有不返回数据的请求,因为客户端对何时发送请求做了控制,对资源的消耗较少。该方法也是可用在所有的浏览器上,不需要服务器端的特殊功能。
2. 缺点:当累积在服务器端的事件需要传送给客户端时,你却一点都不知道,因为这需要一个客户端行为来请求它们。
很明显“捎带轮询(piggyback polling)”这个方法完全是扯淡,因为如果服务器有更新,而你恰恰暂时不需要访问服务器,那么你就永远得不到更新,这个方法太被动了!!
综上所述,在现有的条件下,轮询和捎带轮询已经不得已的最佳解决方案了。
我们回到原点,为什么我们要关闭连接然后再不停的建立连接呢?我们为什么不建立一个时间非常长的连接呢?这是因为以前的HTTP 1.0不支持长连接,所以我们不得已只能轮询!到了HTTP 1.1开始支持长连接,我们是否可以建立一个长连接来完成这个目的呢?答案是:“Yes”,这个答案有一个名字叫Comet。
Comet指的是一种Web应用程序架构。可以直接说,它不是一种技术,而是一种思想,只是这种思想采用了已有的技术去实现。在这种思想里,客户端(Client)不需要显式地向服务器端(Server)发出请求,Server会在其数据发生变化的时候主动将数据异步发送给Client,从而使Client能够及时更新数据并呈现给用户。它不同于传统的Web,也不同于当前流行的Ajax,这种思想非常架构思想非常适合event-driven(事件驱动)式的Web应用和对交互性及实时性要求很强的应用,比如股票交易,聊天室,Web IM,网游等。
实现Comet,最常见的有下面两种方式:
- 1. HTTP 长轮询(HTTP Long Polling):Javascript在处理完服务器返回的信息后再次发出请求,重新建立连接。不同于一般的Ajax,Javascript请求Server,无数据时Server不中断请求,still loading,在一定时间内,获取到数据后,返回请求,又Javascript获取数据后再次发出请求,由此轮询。需要注意的是请求的间隔时间以及每次请求的最长Loading时间。
缺点:会产生大量的通信量,只能通过增加轮询的时间间隔来减轻Server的压力;
- 2. Iframe结合Htmlfile流(streaming)HTTP 流(HTTP Streaming):通过在页面上嵌入一个隐藏的Iframe,设置其src属性为一个长连接的请求,Server采用flush方式将数据作为前端Javascript函数的参数传递;
缺点:会产生进度条的Loading状态并一直穿着,用户使用体验很不好,在Google Talk中,通过Htmlfile Active解决了IE下的进度条显示问题;保持长期链接也非常耗服务器资源;
反向Ajax的目的是允许服务器端向客户端推送信息。这样如果服务端有数据更新,就直接可以推送给客户端。其实这只是一个“叫法”。本质上还是正向的Ajax,“反向”只是“正向”的基础上的一个特殊情况。
HTML5 WebSocket
在现有的条件下,要完成及时地获得服务器端事件这个目标真是让人绞尽脑汁,麻烦,非常的麻烦!! 那么势必要修改HTTP协议了,于是HTML 5 呼之欲出!
如果说Ajax是浏览器的努力,那么Comet是服务器的努力,最终HTML5就是服务器和浏览器共同的努力。
在HTML5中出现的WebSocket是一种比Comet还要新的反向Ajax技术,WebSocket启用了双向的全双工通信信道,许多浏览器(Firefox、Google Chrome和Safari)都已对此做了支持。连接是通过一个被称为WebSocket握手的HTTP请求打开的,其用到了一些特殊的报头。连接会保持在活动状态,你可以使用JavaScript来写入和接收数据,就像是在使用一个原始的TCP套接口一样。WebSocket URL的起始输入是ws://或是wss://(在SSL上),从这个ws和wss上你联想到什么?你应该想到,WebSocket已经不是建立在HTTP协议之上!
WebSocket优点:WebSocket功能强大、双向、低延迟,且易于处理错误,其不会像Comet长轮询那样有许多的连接,也没有Comet流所具有的一些缺点。它的API也很容易使用,无需另外的层就可以直接使用,而Comet则需要一个很好的库来处理重连接、超时、Ajax请求、确认以及选择不同的传输(Ajax长轮询和jsonp轮询)。
WebSocket的缺点有这些:
1. 是一个来自HTML5的新规范,还没有被所有的浏览器支持。
2. 没有请求作用域(request scope),因为WebSocket是一个TCP套接口而不是一个HTTP请求,有作用域的请求服务,比如说Hibernate的SessionInViewFilter,就不太容易使用。Hibernate是一个持久性框架,其在HTTP请求的外围提供了一个过滤器。在请求开始时,其在请求线程中设定了一个上下文(包括事务和JDBC连接)边界;在请求结束时,过滤器销毁这一上下文。
最终建议
相比于Comet,WebSocket带来了更多的好处。在日常开发中,客户端支持的WebSocket速度更快,且产生较少的请求(从而消耗更少的带宽)。不过,由于并非所有的浏览器都支持WebSocket,因此,对于Reverse Ajax库来说,最好的选择就是能够检测对WebSocket的支持,并且如果不支持WebSocket的话,还能够回退到Comet(长轮询)上。
由于这两种技术需要从所有浏览器中获得最好的做法并保持兼容性,因此我的建议是使用一个客户端的JavaScript库,该库在这些技术之上提供一个抽象层。
参考网页:
http://select.yeeyan.org/view/213582/212487
http://www.w3school.com.cn/ajax/ajax_xmlhttprequest_send.asp