首页 > 代码库 > 也谈---基于 HTTP 长连接的“服务(转载)
也谈---基于 HTTP 长连接的“服务(转载)
这里指讨论基于HTTP的推技术, 诸如flash,applet之类的东西不作分析, 他们就不能说是"纯粹"的浏览器应用了.
首先是一点背景知识, 大家都知道长连接避免了tcp连接的反复建立,能够节省大量资源. 但HTTP天生就是短连接的pull式服务, 这不能说是个缺点, 只是对某些实时性服务而言有点不合适.
目前大部分浏览器和web服务器都支持keep-alive参数, 这一点可以部分解决频繁建立连接的问题.
RFC 2068 明确了浏览器与一服务器的连接数不得超于2个, IE这样办了, Firefox似乎不怎么听话. 这使得浏览器在加载包含大量图片等多媒体内容时必须等待, 也就是理论上讲, 浏览器加载一个页面时,只能同时下载2个文件, 多数包含大量多媒体信息的网页都把这些大量的多媒体文件分布的放在不同域上(可以是同一主机),以使浏览器多线程下载.
这里就会影响我们的目的, 如果是长连接, 这势必会永久的占了一个连接, 只剩一个, 好好考虑吧.
当然浏览器可以修改连接会话数配置, 方法如下. 不过如果是互联网程序, 总不能让人人都改吧.
IE缺省2个, Firefox缺省8个.
Firefox---about:config
network.http.max-connections-per-server.
Internet Explorer---regedit
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings.
add DWORD value under it.
MaxConnectionsPerServer
MaxConnectionsPer1_0Server
方案一. ajax轮询.
任何支持xmlhttprequest对象的浏览器都可使用, 对web服务器不作要求, 原理很简单, ajax定时请求, 得到返回结果后更新页面. 非常简单易行,服务端代码不变. 但其根本不是推, 只是用户看上去像推, 自然对性能没有改进, 实时性也保证不了. 缺点的本身也是优点,没有占浏览器的连接资源, 也没有占web服务器的连接和进(线)程资源, 后文说明.
方案二. ajax长轮询.
似乎比方案一好一点, xmlhttprequest请求服务, web服务器再没有更新数据时不返回,等待有新数据时才返回数据给浏览器, 服务端代码需要使用wait(),notify()方法等待和唤醒http处理进(线)程. 浏览器在得到数据或超时返回. 这样避免了多次请求,建立连接却得到没有数据更新的结果而资源浪费. IE中由于缺乏对Streaming AJAX的支持, 在数据返回后必须重新建立连接, 而firefox则可以在 XMLHttpRequest.readystate 为 3 时(数据仍在传输中)读取数据,从而无须关闭连接,就能读取处理服务器端返回的信息。 其实这个也不是推, 只是一个长拉, 但已经比方案一进步了不少. 但它占了浏览器的连接资源, 同时也占了web服务器的连接和进(线)程资源.
方案三.基于 Iframe 及 htmlfile 的流(streaming)方式
iframe 是很早就存在的一种 HTML 标记, 通过在 HTML 页面里嵌入一个隐蔵帧,然后将这个隐蔵帧的 SRC 属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。 这个似乎更好, 不需要xmlhttprequest对象, 而且这个连接理论上是永久的连接, 不会中断. 原理倒也不复杂(一些防火墙常被设置为丢弃过长的连接, 服务器端可以设置一个超时时间, 超时后通知客户端重新建立连接,并关闭原来的连接).
Iframe.src引用的web应用返回一个script脚本, 此脚本更新父页面内容.
这里有个基本概念.chunk 就是分块, 大家都用过下载工具下载过大文件, 这些工具的基本原理就是把大文件分成块,多线程下载, http协议里有块这个概念, 使得浏览器可以边下载边显示. 于是服务端代码可以有一个循环返回script块, 也很简单就是需要flush罢了. 此javascript执行, 更新父页. 方案二中Firefox Streaming AJAX与这个是一致的, 不过是这个被绝大多数浏览器支持. 与方案二一样, 此占浏览器和web服务器连接资源.
但使用这种方案,IE、Morzilla Firefox 下端的进度栏都会显示加载没有完成,而且 IE 上方的图标会不停的转动,表示加载正在进行。一个称为“htmlfile”的 ActiveX 解决了在 IE 中的加载显示问题。Alex Russell 在 “What else is burried down in the depth‘s of Google‘s amazing JavaScript?”文章中介绍了这种方法。Zeitoun 网站提供的 comet-iframe.tar.gz,封装了一个基于 iframe 和 htmlfile 的 JavaScript comet 对象,支持 IE、Mozilla Firefox 浏览器,可以作为参考. 示例为PHP代码, 当然换成别的也行.
方案二与三都需要注意一些问题.
1,因为其占用浏览器连接, 而应用最广泛的IE缺省只能用两个连接, 故不要在同一客户端同时使用超过两个的 HTTP 长连接.
2,服务器端的性能和可扩展性, 这个很重要,作为大用户的web应用, 如果使用长连接, 对服务器性能是个很大的挑战, 保持大量的连接数一般没有什么问题,一般而言, 单服务器可以支持数千个连接, 但是在传统WEB服务器实现采用了阻塞式I/O, 这种模式对每个连接使用一个进(线)程,服务器很难支撑大量的进(线)程. 所幸很多服务器都有了支持comet的方案, 其实原理很简单,就是非阻塞IO,Java中就是常说的NIO, windows,Unix-like当然更有支持. Jetty, tomcat6,glassfish等服务器都有支持.
3控制信息与数据信息使用不同的 HTTP 连接
使用长连接时,存在一个很常见的场景:客户端网页需要关闭,而服务器端还处在读取数据的堵塞状态,客户端需要及时通知服务器端关闭数据连接。服务器在收到关闭请求后首先要从读取数据的阻塞状态唤醒,然后释放为这个客户端分配的资源,再关闭连接。
所以在设计上,我们需要使客户端的控制请求和数据请求使用不同的 HTTP 连接,才能使控制请求不会被阻塞。
在实现上,如果是基于 iframe 流方式的长连接,客户端页面需要使用两个 iframe,一个是控制帧,用于往服务器端发送控制请求,控制请求能很快收到响应,不会被堵塞;一个是显示帧,用于往服务器端发送长连接请求。如果是基于 AJAX 的长轮询方式,客户端可以异步地发出一个 XMLHttpRequest 请求,通知服务器端关闭数据连接。
4在客户和服务器之间保持“心跳”信息
在浏览器与服务器之间维持一个长连接会为通信带来一些不确定性:因为数据传输是随机的,客户端不知道何时服务器才有数据传送。服务器端需要确保当客户端不再工作时,释放为这个客户端分配的资源,防止内存泄漏。因此需要一种机制使双方知道大家都在正常运行。在实现上1).服务器端在阻塞读时会设置一个时限,超时后阻塞读调用会返回,同时发给客户端没有新数据到达的心跳信息。此时如果客户端已经关闭,服务器往通道写数据会出现异常,服务器端就会及时释放为这个客户端分配的资源。
2).如果客户端使用的是基于 AJAX 的长轮询方式;服务器端返回数据、关闭连接后,经过某个时限没有收到客户端的再次请求,会认为客户端不能正常工作,会释放为这个客户端分配、维护的资源。
3).当服务器处理信息出现异常情况,需要发送错误信息通知客户端,同时释放资源、关闭连接。
最后一句, 其实所有所有这些, 都不是推的方案, HTTP的规范确定了请求/回复, 这些不过是看上去像推而矣, 当然后两者确实有推的意思, 如果不算最早一次请求的话^-^.
也谈---基于 HTTP 长连接的“服务(转载)