nuxt 中使用 webSocket,pm2 部署多节点时 使用 redis 通讯

// nuxt.config.ts
nitro: {
	experimental: {
	  websocket: true,
	},
},
// server/routes/xxx.ts
let wsInstance: any | null = null
export default defineWebSocketHandler({
  open(peer) {
    peer.send('')
    wsInstance = peer
  },
})

export function sendMeg(meg: string) {
  if (wsInstance) {
    wsInstance.send(meg)
  }
}

如果有 nginx 转发, 在 nginx 代理中增加:

# WebSocket 支持                                                                                                        
        proxy_http_version 1.1;  # 必须使用 HTTP/1.1                                                                
        proxy_set_header Upgrade $http_upgrade;  # 允许协议升级                                                                 
        proxy_set_header Connection "upgrade";  # 设置连接类型为升级

完整的

location / {                                                                                                                
        proxy_pass  http://127.0.0.1:3000;                                                                                      
        proxy_set_header Host $host;                                                                                            
        proxy_set_header X-Real-IP $remote_addr;                                                                                
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;                                                            
        proxy_set_header X-Forwarded-Proto $scheme;                                                                             
        # WebSocket 支持                                                                                                        
        proxy_http_version 1.1;  # 必须使用 HTTP/1.1                                                                            
        proxy_set_header Upgrade $http_upgrade;  # 允许协议升级                                                                 
        proxy_set_header Connection "upgrade";  # 设置连接类型为升级                                                            
    }

pm2 部署多节点时 使用 redis 通讯

// /routes/process.ts
// 定义 WebSocket 接口
import { publish, subscribe } from '~/utils/redisPubSub'

// 使用 Set 存储所有活动的 WebSocket 连接
const wsConnections = new Set<WebSocketPeer>()
const KEY_PROCESS_MESSAGE = 'process:message'

function broadcastMessage(message: string) {
  let successCount = 0
  let failCount = 0

  wsConnections.forEach((ws) => {
    try {
      ws.send(message)
      successCount++
    }
    catch (error) {
      console.error('Failed to send message to a client:', error)
      failCount++
      // 如果发送失败,可能连接已断开,清理该连接
      wsConnections.delete(ws)
    }
  })

  console.log(`Broadcast complete - Success: ${successCount}, Failed: ${failCount}`)
}

subscribe(KEY_PROCESS_MESSAGE, broadcastMessage)

interface WebSocketPeer {
  send: (data: any) => void
  publish?: (channel: string, data: any) => void
}

// Define WebSocket handler
export default defineWebSocketHandler({
  open(peer) {
    wsConnections.add(peer)
    console.log('WebSocket connection opened.')
  },
  close(peer) {
    wsConnections.delete(peer)
    console.log(`Remaining connections: ${wsConnections.size}`)
  },
  error(peer, error) {
    console.error('WebSocket error:', error)
    wsConnections.delete(peer)
    console.log(`Connections after error: ${wsConnections.size}`)
  },
})

export function sendMsg(msg: string) {
  try {
    publish(KEY_PROCESS_MESSAGE, msg)
  }
  catch (error) {
    console.error('Failed to send message:', error)
  }
}

// utils/redisPubSub
import Redis from 'ioredis'

// 添加连接配置接口
interface RedisConfig {
  host?: string
  port?: number
  url?: string
}

function createRedisClient(config: RedisConfig = {}) {
  const client = config.url
    ? new Redis(config.url)
    : new Redis({
      host: config.host || 'localhost',
      port: config.port || 6379,
    })

  client.on('error', (err) => {
    console.error('Redis client error:', err)
  })

  client.on('connect', () => {
    console.log('Redis client connected')
  })

  return client
}

const redisPublisher = createRedisClient()
const redisSubscriber = createRedisClient()

// 改进订阅函数,添加错误处理和取消订阅功能
function subscribe(channel: string, cb: (message: string) => void) {
  return new Promise((resolve, reject) => {
    redisSubscriber.subscribe(channel, (err, count) => {
      if (err) {
        reject(err)
        return
      }
      console.log(`Subscribed to ${channel}. Total channels: ${count}`)
      resolve(count)
    })

    redisSubscriber.on('message', (ch, message) => {
      if (ch === channel) {
        cb(message)
      }
    })
  })
}

// 改进发布函数,添加错误处理
async function publish(channel: string, message: string) {
  try {
    const result = await redisPublisher.publish(channel, message)
    return result
  }
  catch (error) {
    console.error(`Failed to publish to ${channel}:`, error)
    throw error
  }
}

// 添加清理函数
function cleanup() {
  redisPublisher.disconnect()
  redisSubscriber.disconnect()
}

export { redisPublisher, redisSubscriber, publish, subscribe, cleanup }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值