// 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 }