2. WebSocket實時通信系統
功能概述
實現完整的WebSocket通信系統,支持實時消息推送、連接管理、心跳檢測和自動重連。
技術難點
- WebSocket連接生命周期管理
- 消息序列化和反序列化
- 心跳機制和連接保活
- 錯誤處理和重連策略
- 多組件狀態同步
實現思路
2.1 WebSocket管理器
// src/utils/websocket.ts
export interface WebSocketOptions {url: stringonConnect?: () => voidonDisconnect?: () => voidonMessage?: (message: any) => voidonError?: (error: any) => void
}export class WebSocketManager {private ws: WebSocket | null = nullprivate options: WebSocketOptionsprivate reconnectAttempts = 0private maxReconnectAttempts = 5private reconnectInterval = 1000private heartbeatInterval: NodeJS.Timeout | null = nullconstructor(options: WebSocketOptions) {this.options = options}// 連接WebSocketconnect(): Promise<void> {return new Promise((resolve, reject) => {try {this.ws = new WebSocket(this.options.url)this.ws.onopen = () => {console.log('? WebSocket 連接成功')this.reconnectAttempts = 0this.startHeartbeat()this.options.onConnect?.()resolve()}this.ws.onclose = (event) => {console.log('🔌 WebSocket 連接斷開:', event.code, event.reason)this.stopHeartbeat()this.options.onDisconnect?.()// 自動重連邏輯if (this.reconnectAttempts < this.maxReconnectAttempts) {this.reconnectAttempts++setTimeout(() => {this.connect().catch(console.error)}, this.reconnectInterval * this.reconnectAttempts)}}this.ws.onerror = error => {console.error('? WebSocket 錯誤:', error)this.options.onError?.(error)reject(error)}this.ws.onmessage = event => {try {const data = JSON.parse(event.data)this.options.onMessage?.(data)} catch (err) {console.warn('? 消息解析失敗:', event.data)this.options.onMessage?.(event.data)}}} catch (error) {console.error('? WebSocket 連接失敗:', error)reject(error)}})}// 發送消息send(data: any): void {if (this.ws && this.ws.readyState === WebSocket.OPEN) {const message = typeof data === 'string' ? data : JSON.stringify(data)this.ws.send(message)} else {throw new Error('WebSocket未連接')}}// 心跳機制private startHeartbeat(): void {this.heartbeatInterval = setInterval(() => {if (this.ws && this.ws.readyState === WebSocket.OPEN) {this.send({ type: 'heartbeat', timestamp: Date.now() })}}, 30000) // 30秒發送一次心跳}private stopHeartbeat(): void {if (this.heartbeatInterval) {clearInterval(this.heartbeatInterval)this.heartbeatInterval = null}}// 斷開連接disconnect(): void {this.stopHeartbeat()if (this.ws) {this.ws.close()this.ws = null}}// 獲取連接狀態get isConnected(): boolean {return this.ws?.readyState === WebSocket.OPEN}
}
2.2 Vue組合式API封裝
// src/hooks/useWebSocket.ts
export function useWebSocket(options: WSOptions) {const socketManager = ref<WebSocketManager | null>(null)const isConnected = ref(false)const connect = async (params?: WSOptions) => {if (!useCookie('token').value) returnconst innerParams = params || optionssocketManager.value = createWebSocketManager(typeof innerParams === 'function' ? innerParams() : innerParams)try {await socketManager.value?.connect()isConnected.value = true// 設置斷開連接回調socketManager.value?.setDisconnectCallback(() => {isConnected.value = falseconst { message } = useGlobalComponent()message.error('網絡連接斷開,請刷新頁面')})} catch (error) {console.error('WebSocket連接失敗:', error)isConnected.value = false}}// 發送消息const sendMessage = <T>(data: T) => {if (!socketManager.value?.isConnected) {const { message } = useGlobalComponent()message.error('網絡連接斷開,請刷新頁面')return}socketManager.value?.send(data)}const disconnect = () => {socketManager.value?.disconnect()isConnected.value = false}onUnmounted(disconnect)return {sendMessage,connect,disconnect,isConnected: () => isConnected.value,}
}
2.3 聊天狀態管理集成
// src/stores/chat/methods.ts
export const useChatStore = defineStore('chat', () => {const { sendMessage, connect, disconnect, isConnected } = useWebSocket(() => ({url: `ws://192.168.201.201:8088/api/websocket?token=${useCookie('token').value}`,onMessage: msg => {if (msg.event_id !== state.list[state.list.length - 1]?.event_id) returnstate.list = onMessage(msg, state.list)if (state.isFirst &&[NotificationType.FINISH, NotificationType.END].includes(msg?.data?.type)) {getChatSummary(msg.data.session_id).then(res => {state.title = resstate.isFirst = false})}},}))const send = (data: string, id: number) => {const lastMsg = state.list[state.list.length - 1]let callerInstanceId = ''if (lastMsg && 'caller' in lastMsg) {callerInstanceId = (lastMsg?.caller as { instance_id: string })?.instance_id}const msg = createUserMessage(data, id, callerInstanceId)const question = createResponseMessage(data, id, callerInstanceId)if (state.list.length) {if (state.list[state.list.length - 1]?.session_id === id) {state.list = [...state.list, question]} else {state.list = [question]state.isFirst = true}} else {state.list = [question]state.isFirst = true}sendMessage(msg)return question}return {send,isConnected,}
})
關鍵技術點
- 連接管理: 完整的連接生命周期管理
- 自動重連: 指數退避重連策略
- 心跳機制: 保持連接活躍狀態
- 錯誤處理: 完善的錯誤捕獲和用戶提示
- 狀態同步: 多組件間的連接狀態同步