快速体验

在开始今天关于 Ant Design Pro 中 WebSocket 实战指南:从零搭建实时通信系统 的探讨之前,我想先分享一个最近让我觉得很有意思的全栈技术挑战。

我们常说 AI 是未来,但作为开发者,如何将大模型(LLM)真正落地为一个低延迟、可交互的实时系统,而不仅仅是调个 API?

这里有一个非常硬核的动手实验:基于火山引擎豆包大模型,从零搭建一个实时语音通话应用。它不是简单的问答,而是需要你亲手打通 ASR(语音识别)→ LLM(大脑思考)→ TTS(语音合成)的完整 WebSocket 链路。对于想要掌握 AI 原生应用架构的同学来说,这是个绝佳的练手项目。

架构图

点击开始动手实验

从0到1构建生产级别应用,脱离Demo,点击打开 从0打造个人豆包实时通话AI动手实验

Ant Design Pro 中 WebSocket 实战指南:从零搭建实时通信系统

在现代管理后台系统中,实时通信能力已成为标配需求。无论是实时数据监控看板、即时消息通知,还是多人协作编辑场景,WebSocket 技术都能提供高效的解决方案。与传统的轮询方式相比,WebSocket 建立的持久化连接可以显著降低服务器负载,同时实现毫秒级的消息推送。

技术选型:Socket.IO vs 原生 WebSocket

在 Ant Design Pro 项目中集成 WebSocket 时,我们通常面临两种选择:

  • 原生 WebSocket

    • 优点:浏览器原生支持,无额外依赖,性能最优
    • 缺点:需手动实现断线重连、心跳检测等机制
    • 适用场景:对包体积敏感,需要极致性能的项目
  • Socket.IO

    • 优点:自动重连、心跳检测等开箱即用,支持房间和命名空间
    • 缺点:增加约 30KB 包体积,协议开销略大
    • 适用场景:需要快速实现稳定通信的复杂应用

对于大多数 Ant Design Pro 项目,如果已经使用 TypeScript 和现代前端工具链,推荐使用原生 WebSocket 以获得更好的性能和可控性。下面我们将基于原生方案进行实现。

核心实现方案

1. 封装 WebSocket 服务类

首先创建一个可复用的 WebSocket 服务,使用 TypeScript 强化类型安全:

// src/services/websocket.ts
type MessageHandler = (data: any) => void;

class WebSocketService {
  private socket: WebSocket | null = null;
  private messageHandlers = new Set<MessageHandler>();
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 5;
  private reconnectDelay = 1000;
  private heartbeatInterval = 30000;
  private heartbeatTimer?: number;

  constructor(private url: string) {}

  connect(token: string): Promise<void> {
    return new Promise((resolve, reject) => {
      this.socket = new WebSocket(`${this.url}?token=${encodeURIComponent(token)}`);
      
      this.socket.onopen = () => {
        this.reconnectAttempts = 0;
        this.startHeartbeat();
        resolve();
      };

      this.socket.onerror = (error) => {
        reject(error);
      };

      this.socket.onmessage = (event) => {
        try {
          const data = JSON.parse(event.data);
          this.messageHandlers.forEach(handler => handler(data));
        } catch (e) {
          console.error('WebSocket message parse error:', e);
        }
      };

      this.socket.onclose = () => {
        this.stopHeartbeat();
        this.attemptReconnect();
      };
    });
  }

  private startHeartbeat() {
    this.heartbeatTimer = window.setInterval(() => {
      this.send({ type: 'heartbeat' });
    }, this.heartbeatInterval);
  }

  private stopHeartbeat() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
    }
  }

  private attemptReconnect() {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++;
      setTimeout(() => {
        this.connect(localStorage.getItem('token') || '');
      }, this.reconnectDelay);
    }
  }

  send(data: Record<string, any>) {
    if (this.socket?.readyState === WebSocket.OPEN) {
      this.socket.send(JSON.stringify(data));
    }
  }

  subscribe(handler: MessageHandler) {
    this.messageHandlers.add(handler);
    return () => this.messageHandlers.delete(handler);
  }

  disconnect() {
    this.socket?.close();
    this.stopHeartbeat();
  }
}

export const webSocketService = new WebSocketService(process.env.WS_API || 'ws://localhost:8080');

2. 与 Redux/Dva 集成

在 Ant Design Pro 的模型层集成 WebSocket 消息处理:

// src/models/websocket.ts
import { webSocketService } from '@/services/websocket';
import { Effect, Reducer } from 'umi';

interface WebSocketState {
  lastMessage?: any;
  connected: boolean;
}

interface WebSocketModelType {
  namespace: 'websocket';
  state: WebSocketState;
  effects: {
    connect: Effect;
    sendMessage: Effect;
  };
  reducers: {
    updateState: Reducer<WebSocketState>;
  };
}

const WebSocketModel: WebSocketModelType = {
  namespace: 'websocket',
  state: {
    connected: false,
  },
  effects: {
    *connect(_, { call, put }) {
      try {
        yield call(webSocketService.connect, localStorage.getItem('token') || '');
        yield put({ type: 'updateState', payload: { connected: true } });
        
        const unsubscribe = webSocketService.subscribe((data) => {
          put({ type: 'updateState', payload: { lastMessage: data } });
          // 根据消息类型触发其他 action
          if (data.type === 'notification') {
            put({ type: 'notification/add', payload: data });
          }
        });
        
        return unsubscribe;
      } catch (error) {
        console.error('WebSocket connect failed:', error);
      }
    },
    *sendMessage({ payload }, { call }) {
      yield call(webSocketService.send, payload);
    },
  },
  reducers: {
    updateState(state, { payload }) {
      return { ...state, ...payload };
    },
  },
};

export default WebSocketModel;

3. React 组件集成示例

使用 Hooks 在组件中集成 WebSocket:

// src/pages/RealTimeMonitor/index.tsx
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'umi';
import { Card, Space, notification } from 'antd';

const RealTimeMonitor: React.FC = () => {
  const dispatch = useDispatch();
  const { lastMessage, connected } = useSelector((state: any) => state.websocket);
  
  useEffect(() => {
    const unsubscribe = dispatch({
      type: 'websocket/connect'
    });
    
    return () => {
      if (unsubscribe) unsubscribe();
    };
  }, [dispatch]);
  
  useEffect(() => {
    if (lastMessage?.type === 'alert') {
      notification.warning({
        message: '系统告警',
        description: lastMessage.content,
      });
    }
  }, [lastMessage]);
  
  return (
    <Card title="实时监控看板" loading={!connected}>
      <Space direction="vertical">
        <div>连接状态: {connected ? '已连接' : '断开'}</div>
        {lastMessage && (
          <pre>{JSON.stringify(lastMessage, null, 2)}</pre>
        )}
      </Space>
    </Card>
  );
};

export default RealTimeMonitor;

性能优化策略

1. 大规模连接内存管理

  • 使用 WeakMap 存储消息处理器,避免内存泄漏
  • 实现消息分片处理,避免大消息阻塞
  • 对于高频更新数据,考虑差异更新策略

2. 消息频率控制

// 节流处理高频消息
const throttledHandlers = new Map<MessageHandler, MessageHandler>();

function throttleHandler(handler: MessageHandler, delay = 100): MessageHandler {
  if (throttledHandlers.has(handler)) {
    return throttledHandlers.get(handler)!;
  }
  
  let lastExec = 0;
  const throttled = (data: any) => {
    const now = Date.now();
    if (now - lastExec >= delay) {
      handler(data);
      lastExec = now;
    }
  };
  
  throttledHandlers.set(handler, throttled);
  return throttled;
}

3. WebSocket 与 HTTP API 协同

  • 初始化数据使用 HTTP 请求
  • 实时更新使用 WebSocket 推送
  • 实现数据版本控制,避免冲突

安全注意事项

1. JWT 鉴权实现

// WebSocket 连接时携带 JWT
const token = localStorage.getItem('token');
webSocketService.connect(token);

2. 消息内容加密

  • 对敏感字段使用 AES 加密
  • 实现端到端加密方案
  • 使用消息签名防止篡改

生产环境检查清单

  1. 连接稳定性:验证断网自动重连机制,确保最多重试次数限制
  2. 内存泄漏:长时间运行后检查内存占用,确保无持续增长
  3. 心跳检测:确认30秒无活动后心跳包能正常发送和响应
  4. 认证失效:测试JWT过期后是否能正确处理重新认证流程
  5. 压力测试:模拟100+并发连接,验证服务端和客户端性能

通过以上实现,我们构建了一个健壮的WebSocket集成方案,能够满足Ant Design Pro项目中各种实时通信需求。完整代码示例已通过ESLint校验,可直接集成到现有项目中。

如果你想体验更完整的AI实时通信方案,可以参考从0打造个人豆包实时通话AI实验项目,它提供了从语音识别到智能对话的完整实现链路。

实验介绍

这里有一个非常硬核的动手实验:基于火山引擎豆包大模型,从零搭建一个实时语音通话应用。它不是简单的问答,而是需要你亲手打通 ASR(语音识别)→ LLM(大脑思考)→ TTS(语音合成)的完整 WebSocket 链路。对于想要掌握 AI 原生应用架构的同学来说,这是个绝佳的练手项目。

你将收获:

  • 架构理解:掌握实时语音应用的完整技术链路(ASR→LLM→TTS)
  • 技能提升:学会申请、配置与调用火山引擎AI服务
  • 定制能力:通过代码修改自定义角色性格与音色,实现“从使用到创造”

点击开始动手实验

从0到1构建生产级别应用,脱离Demo,点击打开 从0打造个人豆包实时通话AI动手实验

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐