帅帅的技术博客
·4 min read

WebSocket 实时通信架构设计

分享客服聊天系统中 WebSocket 实时通信的架构设计与实现经验。

#WebSocket#实时通信#Vue#Node.js

WebSocket 实时通信架构设计

在客服聊天系统项目中,我负责设计和实现了基于 WebSocket 的实时通信架构。今天分享一下核心设计思路。

系统架构

访客端 ←→ WebSocket Server ←→ 客服端
                ↓
            消息队列 (Redis)
                ↓
            数据持久化 (MySQL)

核心功能实现

1. 连接管理

// WebSocket 连接管理
class ConnectionManager {
  constructor() {
    this.connections = new Map() // userId -> ws
  }

  add(userId, ws) {
    this.connections.set(userId, ws)
  }

  remove(userId) {
    this.connections.delete(userId)
  }

  sendTo(userId, message) {
    const ws = this.connections.get(userId)
    if (ws && ws.readyState === WebSocket.OPEN) {
      ws.send(JSON.stringify(message))
    }
  }
}

2. 消息类型设计

interface Message {
  type: 'chat' | 'system' | 'transfer' | 'close'
  from: string
  to: string
  content: string
  timestamp: number
}

3. Vuex 状态管理

// store/modules/chat.js
export default {
  state: {
    conversations: [],
    currentConversation: null,
    messages: []
  },

  actions: {
    // 接收新消息
    receiveMessage({ commit, state }, message) {
      commit('ADD_MESSAGE', message)
      
      // 更新对话列表
      const conversation = state.conversations.find(
        c => c.id === message.conversationId
      )
      if (conversation) {
        conversation.lastMessage = message
        conversation.unread++
      }
    },

    // 发送消息
    async sendMessage({ commit }, message) {
      commit('ADD_MESSAGE', { ...message, sending: true })
      
      try {
        await socket.emit('send-message', message)
        commit('UPDATE_MESSAGE_STATUS', { id: message.id, status: 'sent' })
      } catch (error) {
        commit('UPDATE_MESSAGE_STATUS', { id: message.id, status: 'failed' })
      }
    }
  }
}

关键场景处理

1. 断线重连

class SocketClient {
  constructor() {
    this.reconnectAttempts = 0
    this.maxReconnectAttempts = 5
  }

  connect() {
    this.ws = new WebSocket(this.url)
    
    this.ws.onclose = () => {
      if (this.reconnectAttempts < this.maxReconnectAttempts) {
        setTimeout(() => {
          this.reconnectAttempts++
          this.connect()
        }, 3000 * this.reconnectAttempts)
      }
    }

    this.ws.onopen = () => {
      this.reconnectAttempts = 0
    }
  }
}

2. 心跳检测

// 心跳保活
setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'ping' }))
  }
}, 30000)

3. 消息确认机制

// 消息回执
const pendingMessages = new Map()

function sendWithAck(message) {
  return new Promise((resolve, reject) => {
    pendingMessages.set(message.id, { resolve, reject })
    ws.send(JSON.stringify(message))
    
    // 超时处理
    setTimeout(() => {
      if (pendingMessages.has(message.id)) {
        pendingMessages.delete(message.id)
        reject(new Error('Timeout'))
      }
    }, 10000)
  })
}

// 收到回执
ws.onmessage = (event) => {
  const data = JSON.parse(event.data)
  if (data.type === 'ack' && pendingMessages.has(data.messageId)) {
    pendingMessages.get(data.messageId).resolve()
    pendingMessages.delete(data.messageId)
  }
}

性能优化

1. 消息分页加载

// 分页获取历史消息
async loadHistory(conversationId, page = 1) {
  const messages = await api.getMessages({
    conversationId,
    page,
    pageSize: 20
  })
  
  this.messages.unshift(...messages)
}

2. 虚拟列表渲染

对于大量消息的场景,使用虚拟列表优化性能:

<virtual-list
  :items="messages"
  :item-height="60"
  v-slot="{ item }"
>
  <message-item :message="item" />
</virtual-list>

总结

WebSocket 实时通信的关键点:

  1. 连接稳定性:断线重连、心跳保活
  2. 消息可靠性:确认机制、重试策略
  3. 状态管理:统一的状态管理避免数据不一致
  4. 性能优化:分页加载、虚拟列表

这套架构在实际项目中稳定运行,支持了日均数万次的会话量。


有问题欢迎交流!

感谢阅读!如有问题,欢迎交流讨论。

返回文章列表