最后更新于 November 5th, 2012 (Spring MVC 3.2 RC1)
在我上一篇帖子中,我介绍了 Spring MVC 3.2 中基于 Servlet 3 的新的异步支持,并讨论了长时间运行的请求。异步处理的第二个非常重要的动机是浏览器需要接收实时更新。例如浏览器聊天、股票行情、状态更新、实时体育赛果等。当然,并非所有示例都对延迟同样敏感,但它们都具有类似的需求。
在标准的 HTTP 请求-响应语义中,浏览器发起请求,服务器发送响应,这意味着服务器在收到来自浏览器的请求之前不能发送新的信息。已经发展出了几种方法,包括传统轮询、长轮询和HTTP 流,最近我们还有了WebSocket协议。
传统轮询
浏览器不断发送请求以检查是否有新信息,服务器每次都会立即响应。这适用于可以在相对稀疏的间隔进行轮询的场景。例如,邮件客户端可以每 10 分钟检查一次新邮件。这种方法简单有效。然而,当需要尽快显示新信息时,这种方法就变得低效了,在这种情况下轮询必须非常频繁。
长轮询
浏览器不断发送请求,但服务器直到有新信息要发送时才响应。从客户端的角度来看,这与传统轮询相同。从服务器的角度来看,这与长时间运行的请求非常相似,可以使用第一部分讨论的技术进行扩展。
响应可以保持开启多长时间?浏览器通常设置为在 5 分钟后超时,而代理等网络中介可能会更快超时。因此,即使没有新信息到达,长轮询请求也应定期完成,以便浏览器发送新的请求。此 IETF 文档建议使用 30 到 120 秒之间的超时值,但实际使用的值可能取决于您对分隔浏览器和服务器的网络中介的控制程度。
长轮询可以显著减少接收低延迟信息更新所需的请求数量,特别是在新信息以不规则间隔可用的情况下。然而,更新越频繁,它就越接近传统轮询。
HTTP 流
浏览器向服务器发送请求,服务器在有信息要发送时响应。然而,与长轮询不同,服务器保持响应开启状态,并随着新信息的到达继续发送更多更新。这种方法消除了轮询的需要,但也更显著地偏离了典型的 HTTP 请求-响应语义。例如,客户端和服务器需要就如何解释响应流达成一致,以便客户端知道一个更新在哪里结束,下一个更新在哪里开始。此外,网络中介可能会缓存响应流,这阻碍了这种方法的意图。这就是为什么长轮询在今天更常被使用的原因。
WebSocket 协议
浏览器向服务器发送 HTTP 请求,请求切换到 WebSocket 协议,服务器通过确认升级来响应。此后,浏览器和服务器可以通过 TCP socket 在两个方向上发送数据帧。
WebSocket 协议旨在取代轮询的需求,特别适用于浏览器和服务器之间需要高频率交换消息的场景。通过 HTTP 进行的初始握手确保 WebSocket 请求可以穿过防火墙。然而,也存在重大挑战,因为大多数已部署的浏览器不支持 WebSockets,并且在穿过网络中介方面存在进一步的问题。
WebSockets 围绕着文本或二进制消息的双向交换。这与 RESTful、基于 HTTP 的架构相比,导致了显著不同的方法。事实上,需要在 WebSockets 之上运行一些其他协议,例如 XMPP、AMQP、STOMP 或其他,哪一个或哪些协议将成为主流还有待观察。
WebSocket 协议已经由 IETF 标准化,而 WebSocket API 正在由 W3C 进行最终阶段的标准化。许多 Java 实现已经可用,包括 Jetty 和 Tomcat 等 Servlet 容器。Servlet 3.1 规范可能会支持初始的 WebSocket 升级请求,而一个单独的 JSR-356 将定义一个基于 Java 的 WebSocket API。
回到 Spring MVC 3.2,Servlet 3 的异步功能可用于长时间运行的请求以及 HTTP 流,Filip Hanik 将其称为“服务器端的客户端 AJAX 调用”。至于 WebSockets,Spring 3.2 尚未支持,但很可能会包含在 Spring 3.3 中。您可以关注 SPR-9356 获取进展更新。
下一篇帖子将转向示例代码,并更详细地解释新的 Spring MVC 3.2 功能。