12 KiB
12 KiB
WebSocket 功能文档
概述
本项目基于 Laravel-S 和 Swoole 实现了完整的 WebSocket 功能,支持实时通信、消息推送、广播等功能。
功能特性
- ✅ 实时双向通信
- ✅ 用户连接管理
- ✅ 点对点消息发送
- ✅ 群发消息/广播
- ✅ 频道订阅/取消订阅
- ✅ 心跳机制
- ✅ 自动重连
- ✅ 在线状态管理
- ✅ 系统通知推送
- ✅ 数据更新推送
架构设计
后端组件
1. WebSocketHandler (app/Services/WebSocket/WebSocketHandler.php)
WebSocket 处理器,实现了 Swoole 的 WebSocketHandlerInterface 接口。
主要方法:
onOpen(): 处理连接建立事件onMessage(): 处理消息接收事件onClose(): 处理连接关闭事件
支持的消息类型:
ping/pong: 心跳检测heartbeat: 心跳确认chat: 私聊消息broadcast: 广播消息subscribe/unsubscribe: 频道订阅/取消订阅
2. WebSocketService (app/Services/WebSocket/WebSocketService.php)
WebSocket 服务类,提供便捷的 WebSocket 操作方法。
主要方法:
sendToUser($userId, $data): 发送消息给指定用户sendToUsers($userIds, $data): 发送消息给多个用户broadcast($data, $excludeUserId): 广播消息给所有用户sendToChannel($channel, $data): 发送消息给指定频道getOnlineUserCount(): 获取在线用户数isUserOnline($userId): 检查用户是否在线sendSystemNotification(): 发送系统通知pushDataUpdate(): 推送数据更新
3. WebSocketController (app/Http/Controllers/System/WebSocket.php)
WebSocket API 控制器,提供 HTTP 接口用于管理 WebSocket 连接。
前端组件
WebSocketClient (resources/admin/src/utils/websocket.js)
WebSocket 客户端封装类。
功能:
- 自动连接和重连
- 心跳机制
- 消息类型路由
- 事件监听
- 连接状态管理
配置说明
Laravel-S 配置 (config/laravels.php)
'websocket' => [
'enable' => env('LARAVELS_WEBSOCKET', true),
'handler' => \App\Services\WebSocket\WebSocketHandler::class,
],
'swoole_tables' => [
'wsTable' => [
'size' => 102400,
'column' => [
['name' => 'value', 'type' => \Swoole\Table::TYPE_STRING, 'size' => 1024],
['name' => 'expiry', 'type' => \Swoole\Table::TYPE_INT, 'size' => 4],
],
],
],
环境变量
在 .env 文件中添加:
LARAVELS_WEBSOCKET=true
API 接口
1. 获取在线用户数
GET /admin/websocket/online-count
响应:
{
"code": 200,
"message": "success",
"data": {
"online_count": 10
}
}
2. 获取在线用户列表
GET /admin/websocket/online-users
响应:
{
"code": 200,
"message": "success",
"data": {
"user_ids": [1, 2, 3, 4, 5],
"count": 5
}
}
3. 检查用户在线状态
POST /admin/websocket/check-online
请求参数:
{
"user_id": 1
}
响应:
{
"code": 200,
"message": "success",
"data": {
"user_id": 1,
"is_online": true
}
}
4. 发送消息给指定用户
POST /admin/websocket/send-to-user
请求参数:
{
"user_id": 1,
"type": "notification",
"data": {
"title": "新消息",
"message": "您有一条新消息"
}
}
5. 发送消息给多个用户
POST /admin/websocket/send-to-users
请求参数:
{
"user_ids": [1, 2, 3],
"type": "notification",
"data": {
"title": "系统通知",
"message": "系统将在今晚进行维护"
}
}
6. 广播消息
POST /admin/websocket/broadcast
请求参数:
{
"type": "notification",
"data": {
"title": "公告",
"message": "欢迎使用新版本"
},
"exclude_user_id": 1 // 可选:排除某个用户
}
7. 发送消息到频道
POST /admin/websocket/send-to-channel
请求参数:
{
"channel": "orders",
"type": "data_update",
"data": {
"order_id": 123,
"status": "paid"
}
}
8. 发送系统通知
POST /admin/websocket/send-notification
请求参数:
{
"title": "系统维护",
"message": "系统将于今晚 23:00-24:00 进行维护",
"type": "warning",
"extra_data": {
"start_time": "23:00",
"end_time": "24:00"
}
}
9. 发送通知给指定用户
POST /admin/websocket/send-notification-to-users
请求参数:
{
"user_ids": [1, 2, 3],
"title": "订单更新",
"message": "您的订单已发货",
"type": "success"
}
10. 推送数据更新
POST /admin/websocket/push-data-update
请求参数:
{
"user_ids": [1, 2, 3],
"resource_type": "order",
"action": "update",
"data": {
"id": 123,
"status": "shipped"
}
}
11. 推送数据更新到频道
POST /admin/websocket/push-data-update-channel
请求参数:
{
"channel": "orders",
"resource_type": "order",
"action": "create",
"data": {
"id": 124,
"customer": "张三",
"amount": 100.00
}
}
12. 断开用户连接
POST /admin/websocket/disconnect-user
请求参数:
{
"user_id": 1
}
前端使用示例
1. 基本连接
import { getWebSocket, closeWebSocket } from '@/utils/websocket'
import { useUserStore } from '@/stores/modules/user'
const userStore = useUserStore()
// 连接 WebSocket
const ws = getWebSocket(userStore.userInfo.id, userStore.token, {
onOpen: (event) => {
console.log('WebSocket 已连接')
},
onMessage: (message) => {
console.log('收到消息:', message)
},
onError: (error) => {
console.error('WebSocket 错误:', error)
},
onClose: (event) => {
console.log('WebSocket 已关闭')
}
})
// 连接
ws.connect()
2. 监听特定消息类型
// 监听通知消息
ws.on('notification', (data) => {
message.success(data.title, data.message)
})
// 监听数据更新
ws.on('data_update', (data) => {
console.log('数据更新:', data.resource_type, data.action)
// 刷新数据
loadData()
})
3. 发送消息
// 发送心跳
ws.send('heartbeat', { timestamp: Date.now() })
// 发送私聊消息
ws.send('chat', {
to_user_id: 2,
content: '你好,这是一条私聊消息'
})
// 订阅频道
ws.send('subscribe', { channel: 'orders' })
// 取消订阅
ws.send('unsubscribe', { channel: 'orders' })
4. 发送广播消息
ws.send('broadcast', {
message: '这是一条广播消息'
})
5. 断开连接
// 断开连接
ws.disconnect()
// 或使用全局方法
closeWebSocket()
6. 在 Vue 组件中使用
<template>
<div>
<a-button @click="connectWebSocket">连接 WebSocket</a-button>
<a-button @click="disconnectWebSocket">断开连接</a-button>
<a-button @click="sendMessage">发送消息</a-button>
<div>连接状态: {{ connectionStatus }}</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { getWebSocket } from '@/utils/websocket'
import { useUserStore } from '@/stores/modules/user'
const userStore = useUserStore()
const ws = ref(null)
const connectionStatus = ref('未连接')
const connectWebSocket = () => {
ws.value = getWebSocket(userStore.userInfo.id, userStore.token, {
onOpen: () => {
connectionStatus.value = '已连接'
},
onMessage: (message) => {
handleMessage(message)
},
onClose: () => {
connectionStatus.value = '已断开'
}
})
ws.value.connect()
}
const disconnectWebSocket = () => {
if (ws.value) {
ws.value.disconnect()
connectionStatus.value = '已断开'
}
}
const sendMessage = () => {
if (ws.value && ws.value.isConnected) {
ws.value.send('chat', {
to_user_id: 2,
content: '测试消息'
})
}
}
const handleMessage = (message) => {
switch (message.type) {
case 'notification':
message.success(message.data.title, message.data.message)
break
case 'data_update':
// 处理数据更新
break
case 'chat':
// 处理聊天消息
break
}
}
onMounted(() => {
connectWebSocket()
})
onUnmounted(() => {
disconnectWebSocket()
})
</script>
消息格式
服务端发送的消息格式
{
"type": "notification",
"data": {
"title": "标题",
"message": "内容",
"type": "info",
"timestamp": 1641234567
}
}
客户端发送的消息格式
{
"type": "chat",
"data": {
"to_user_id": 2,
"content": "消息内容"
}
}
启动和停止
启动 Laravel-S 服务
php bin/laravels start
停止 Laravel-S 服务
php bin/laravels stop
重启 Laravel-S 服务
php bin/laravels restart
重载 Laravel-S 服务(平滑重启)
php bin/laravels reload
查看服务状态
php bin/laravels status
WebSocket 连接地址
开发环境
ws://localhost:5200/ws?user_id={user_id}&token={token}
生产环境
wss://yourdomain.com/ws?user_id={user_id}&token={token}
Nginx 配置示例
server {
listen 80;
server_name yourdomain.com;
root /path/to/your/project/public;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# WebSocket 代理配置
location /ws {
proxy_pass http://127.0.0.1:5200;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
使用场景
1. 实时通知
// 发送系统通知
$webSocketService->sendSystemNotification(
'系统维护',
'系统将于今晚进行维护',
'warning'
);
2. 订单状态更新
// 推送订单状态更新给相关人员
$webSocketService->pushDataUpdate(
[$order->user_id],
'order',
'update',
[
'id' => $order->id,
'status' => $order->status,
'updated_at' => $order->updated_at
]
);
3. 实时聊天
// 发送私聊消息
ws.send('chat', {
to_user_id: 2,
content: '你好'
})
4. 数据监控
// 推送系统监控数据到特定频道
$webSocketService->sendToChannel('system_monitor', 'monitor', [
'cpu_usage' => 75,
'memory_usage' => 80,
'disk_usage' => 60
]);
注意事项
- 连接认证: WebSocket 连接时需要提供
user_id和token参数 - 心跳机制: 客户端默认每 30 秒发送一次心跳
- 自动重连: 连接断开后会自动尝试重连,最多重试 5 次
- 并发限制: Swoole Table 最多支持 102,400 个连接
- 内存管理: 注意内存泄漏问题,定期重启服务
- 安全性: 生产环境建议使用 WSS (WebSocket Secure)
- 日志监控: 查看日志文件
storage/logs/swoole-YYYY-MM.log
故障排查
1. 无法连接 WebSocket
- 检查 Laravel-S 服务是否启动
- 检查端口 5200 是否被占用
- 检查防火墙设置
- 查看日志文件
2. 连接频繁断开
- 检查网络稳定性
- 调整心跳间隔
- 检查服务器资源使用情况
3. 消息发送失败
- 检查用户是否在线
- 检查消息格式是否正确
- 查看错误日志
参考资料
更新日志
2024-02-08
- ✅ 初始版本发布
- ✅ 实现基础 WebSocket 功能
- ✅ 实现消息推送功能
- ✅ 实现频道订阅功能
- ✅ 实现前端客户端封装
- ✅ 实现管理 API 接口