1. AJAX服务器推送技术概述
在传统Web应用中,客户端需要不断向服务器发送请求以获取最新数据,这种"一问一答"的模式存在明显的实时性缺陷。2005年,Jessie James Garrett首次提出AJAX(Asynchronous JavaScript and XML)概念后,开发者们很快发现标准AJAX仍然无法实现真正的服务器主动推送。于是,一系列基于HTTP长连接的服务器推送技术应运而生。
我在实际项目中首次接触这类技术是在开发一个实时股票行情系统时。当时发现简单的定时轮询会导致:
- 无效请求过多(行情未更新时仍在频繁查询)
- 数据延迟高达15-30秒
- 服务器负载激增(3000+并发用户时CPU使用率超80%)
这促使我深入研究了几种主流的服务器推送方案,它们本质上都是对HTTP协议的"创造性使用"。
2. 核心技术原理对比
2.1 长轮询(Long Polling)
这是最易实现的方案。当客户端发起请求后:
- 服务器保持连接打开直到有数据可发送
- 返回响应后客户端立即发起新请求
- 循环往复实现准实时通信
function longPoll() { $.ajax({ url: '/updates', success: function(data) { processData(data); longPoll(); // 立即发起下一次请求 }, timeout: 30000 // 设置超时防止连接僵死 }); }关键点:服务器需要为每个挂起的请求保持线程/进程资源,高并发时需配合事件驱动架构(如Node.js)
2.2 HTTP流(HTTP Streaming)
更高效的实现方式,主要特点:
- 服务器保持连接永久开放
- 通过分块传输编码(Transfer-Encoding: chunked)持续发送数据
- 客户端通过监听readyState==3状态处理增量数据
var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if(xhr.readyState == 3) { var newData = xhr.responseText.substr(lastIndex); processIncrementalData(newData); lastIndex = xhr.responseText.length; } }; xhr.open('GET', '/stream'); xhr.send();2.3 服务端实现差异
以Node.js为例展示两种实现差异:
| 方案 | 代码示例 | 资源消耗 |
|---|---|---|
| 长轮询 | res.write(data); res.end(); | 中 |
| HTTP流 | res.write(data);(不结束响应) | 低 |
3. 生产环境实战要点
3.1 连接管理策略
在电商实时订单系统中,我们采用以下优化措施:
- 心跳机制:每30秒发送\n\n保持连接活跃
- 超时控制:客户端90秒无响应自动断开
- 重连补偿:断开后携带最后更新时间戳重新连接
// 心跳包实现 setInterval(() => { res.write('\n\n'); }, 30000);3.2 数据格式优化
对比三种数据封装格式的性能表现:
| 格式 | 大小(100条记录) | 解析耗时 |
|---|---|---|
| XML | 12KB | 45ms |
| JSON | 6KB | 15ms |
| Binary | 2KB | 5ms |
我们最终采用JSON分段传输方案:
{ "seq": 12345, "data": [...], "next": "/segment?after=12345" }3.3 服务端压力测试
使用JMeter模拟不同方案的并发表现:
| 并发用户数 | 长轮询CPU使用率 | HTTP流CPU使用率 |
|---|---|---|
| 1000 | 38% | 22% |
| 5000 | 91% | 67% |
| 10000 | 超载 | 89% |
4. 常见问题排查指南
4.1 连接异常断开
典型表现:
- 客户端频繁重连
- 服务器大量TIME_WAIT状态连接
解决方案:
# Nginx配置调整 proxy_read_timeout 180s; proxy_connect_timeout 30s; keepalive_timeout 75s;4.2 内存泄漏问题
在Java服务端发现的典型内存泄漏场景:
// 错误示例:未清理completedRequests集合 static Map<Long, HttpServletResponse> pendingRequests = new ConcurrentHashMap<>(); // 正确做法 public void onComplete(long requestId) { pendingRequests.remove(requestId); // 必须显式移除 }4.3 浏览器兼容性
各浏览器对readystate==3的支持差异:
| 浏览器 | 支持情况 |
|---|---|
| Chrome | 完整支持 |
| Firefox | 需要手动解析xhr.responseText |
| IE11 | 部分支持,需降级到轮询 |
| Safari | 需要设置特殊header |
5. 现代替代方案对比
虽然WebSocket已成为主流,但在某些场景下传统推送仍具优势:
| 维度 | AJAX推送 | WebSocket |
|---|---|---|
| 防火墙兼容性 | 穿透率99.8% | 可能被拦截 |
| 旧系统支持 | 无需升级 | 需后端改造 |
| 数据压缩 | 可复用HTTP压缩 | 需单独实现 |
| 开发复杂度 | 中等 | 较高 |
在金融行业某项目中,我们最终采用混合方案:
- 对公网用户使用HTTP流
- 内网管理系统使用WebSocket
- 通过API网关统一协议转换
这种架构每天稳定处理超过200万条实时价格更新,平均延迟控制在800ms以内。实现过程中最大的教训是:必须为每个连接设置严格的超时控制,我们曾因未及时释放闲置连接导致服务器内存溢出。