开发一个图书馆人流量统计平台:小米电视显示,WebSocket和Ajax轮询那个好?

76 阅读3分钟

让我详细比较WebSocket和Ajax轮询的区别:

1. 服务器开销对比

WebSocket方式:

// WebSocket服务器端代码(已实现)
private readonly Timer _timer = new Timer(async _ => await BroadcastDataAsync(), null, TimeSpan.Zero, TimeSpan.FromSeconds(5));

// 一个连接对应一个socket
private readonly ConcurrentDictionary<string, WebSocket> _sockets = new ConcurrentDictionary<string, WebSocket>();

Ajax轮询方式:

// Ajax API端点
[ApiController]
[Route("api/[controller]")]
public class PeopleCountingController : ControllerBase
{
    [HttpGet]
    public async Task<IActionResult> Get()
    {
        var result = await _service.GetPeopleCountingDataAsync();
        return Ok(result);
    }
}

// 前端轮询代码
setInterval(async () => {
    try {
        const response = await fetch('/api/PeopleCounting');
        const data = await response.json();
        updateUI(data);
    } catch (error) {
        console.error('获取数据失败:', error);
    }
}, 180000); // 180秒

2. 开销比较

WebSocket:

  • 连接开销

    • 初始建立连接时需要握手
    • 之后保持长连接,几乎没有额外开销
    • 每个客户端占用一个服务器socket连接
  • 数据传输开销

    • 头部信息很小(2-14字节)
    • 不需要重复发送HTTP头
    • 双向通信无需建立新连接
  • 服务器资源消耗

    内存占用 = 基础内存 + (每个连接 × 内存开销)
    CPU使用 = 数据处理 + 少量连接维护开销
    

Ajax轮询:

  • 连接开销

    • 每次请求都需要完整的HTTP连接
    • 需要发送完整的HTTP头
    • 连接用完即释放
  • 数据传输开销

    • 每次请求都有完整的HTTP头
    • 重复发送相同的头信息
    • 每次都要建立新的TCP连接
  • 服务器资源消耗

    内存占用 = 基础内存 + (并发请求数 × 请求处理开销)
    CPU使用 = 数据处理 + 频繁的连接建立/释放开销
    

3. 优缺点对比

WebSocket优点:

  1. 实时性好

    // WebSocket可以立即接收数据
    ws.onmessage = (event) => {
        const data = JSON.parse(event.data);
        updateUI(data); // 数据一到达就更新
    };
    
  2. 网络效率高

    • 减少HTTP头开销
    • 减少连接建立/断开的开销
  3. 服务器主动推送

    // 服务器可以主动推送重要更新
    await webSocket.SendAsync(
        new ArraySegment<byte>(data),
        WebSocketMessageType.Text,
        true,
        CancellationToken.None
    );
    
  4. 更少的服务器负载

    • 不需要处理重复的HTTP请求
    • 连接维护成本较低

WebSocket缺点:

  1. 需要维护连接状态

    // 需要处理连接断开和重连
    ws.onclose = () => {
        setTimeout(connect, 5000); // 断开后重连
    };
    
  2. 可能需要心跳机制

    // 需要定期发送心跳保持连接
    private readonly Timer _heartbeatTimer = new Timer(
        async _ => await SendHeartbeat(),
        null,
        TimeSpan.Zero,
        TimeSpan.FromSeconds(30)
    );
    
  3. 某些代理服务器可能不支持

Ajax轮询优点:

  1. 简单易实现

    // 简单的定时请求
    setInterval(fetchData, 180000);
    
  2. 无需特殊服务器支持

    • 标准HTTP请求
    • 兼容性好
  3. 容错性好

    • 请求失败不影响后续请求
    • 无需维护连接状态

Ajax轮询缺点:

  1. 延迟大

    • 最多180秒的数据延迟
    • 不适合实时性要求高的场景
  2. 资源浪费

    // 即使数据没有变化也会请求
    async function fetchData() {
        const response = await fetch('/api/data');
        const data = await response.json();
        // 可能数据没变化
    }
    
  3. 服务器负载高

    • 频繁的HTTP请求
    • 大量的连接建立/断开

4. 使用建议

适合使用WebSocket的场景:

  1. 实时监控数据
  2. 大屏展示
  3. 需要即时更新的数据
  4. 长期保持连接的场景
// WebSocket示例:实时更新人流量
public class WebSocketHandler
{
    private async Task BroadcastDataAsync()
    {
        var data = await _service.GetPeopleCountingDataAsync();
        foreach (var socket in _sockets.Values)
        {
            if (socket.State == WebSocketState.Open)
            {
                await SendDataToClientAsync(socket, data);
            }
        }
    }
}

适合使用Ajax轮询的场景:

  1. 数据更新频率低
  2. 简单的数据刷新
  3. 对实时性要求不高
  4. 需要最大兼容性
// Ajax轮询示例:定时刷新数据
function setupPolling() {
    async function pollData() {
        try {
            const response = await fetch('/api/PeopleCounting');
            if (response.ok) {
                const data = await response.json();
                updateDisplay(data);
            }
        } catch (error) {
            console.error('轮询失败:', error);
        }
    }
    
    setInterval(pollData, 180000);
}

5. 结论

对于图书馆人流量统计系统:

  • 如果是电视大屏展示,建议使用WebSocket
  • 如果是普通的管理后台,Ajax轮询就足够了
  • 可以同时支持两种方式,让客户端根据需求选择

最佳实践是根据具体场景选择合适的方案,或者混合使用两种方式:

public class PeopleCountingController
{
    // 提供两种访问方式
    [HttpGet("poll")]  // Ajax轮询接口
    public async Task<IActionResult> GetData()
    {
        var data = await _service.GetPeopleCountingDataAsync();
        return Ok(data);
    }

    // WebSocket连接由WebSocketHandler处理
}