本文目录导读:

使用 PHP 实现监控大屏,通常并不是指用 PHP 去渲染实时动画(如 WebSocket 推送图表数据),因为 PHP 本身是同步阻塞的语言,不适合做长连接。
在真实的生产环境中,PHP 通常负责后端数据处理和 API 接口,而前端的实时刷新和图表渲染由 JavaScript(如 ECharts、Highcharts) + WebSocket/SSE/Ajax 来完成。
以下是一套经典、可行的 PHP 项目实现监控大屏的方案,分为架构设计、后端实现、前端实现三个部分。
整体架构设计
这是目前最主流的“PHP + 前端实时刷新”方案,而非“PHP 推流”。
graph TD
A[监控数据源<br>服务器/数据库/API] --> B(PHP 后端)
B --> C{数据存储}
C --> D[数据库/Redis]
D --> E(PHP API 接口)
E --> F[前端定时请求 Ajax]
F --> G[JavaScript 大屏渲染<br>ECharts/DataV]
- PHP 角色:聚合数据(从数据库、Redis、外部API拉取)、清洗数据、提供 JSON 接口、鉴权。
- 前端角色:每隔 N 秒(如3-5秒)通过
setInterval或setTimeout请求 PHP 接口,拿最新数据更新图表。
为什么不用 WebSocket? 如果你坚持全栈 PHP,可以使用 Workerman 或 Swoole 扩展让 PHP 支持 WebSocket 推送,但大多数情况下,Ajax 轮询已经满足监控大屏的“准实时”需求(1-5秒延迟)。
PHP 后端实现示例
假设我们有服务器 CPU、内存、在线用户数需要展示。
数据采集与存储 (模拟)
通常通过定时脚本(Crontab)或守护进程采集数据存入数据库或 Redis。
// collect_data.php (定时任务脚本,每分钟执行)
function collectMetrics() {
// 模拟获取服务器负载和用户数
$data = [
'time' => date('Y-m-d H:i:s'),
'cpu' => mt_rand(10, 95),
'memory' => mt_rand(30, 90),
'users' => mt_rand(1000, 5000),
];
// 存入 Redis (推荐用于实时大屏)
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->set('dashboard_cpu', json_encode($data));
// 同时存入 MySQL 用于历史趋势
// DB::table('metrics')->insert($data);
}
// 每60秒执行一次
collectMetrics();
提供 JSON 接口给大屏
这是前端会请求的 API。
// api/dashboard.php?type=current
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
// 请求类型
$type = $_GET['type'] ?? 'current';
if ($type === 'current') {
// 从 Redis 获取最新数据
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$current = $redis->get('dashboard_cpu');
echo $current ?: json_encode(['error' => 'No data']);
} elseif ($type === 'history') {
// 从 MySQL 获取过去24小时数据 (用于折线图)
$history = DB::table('metrics')
->where('time', '>', date('Y-m-d H:i:s', strtotime('-24 hours')))
->get();
echo json_encode($history);
}
聚合多个图表的数据接口
为了减少前端请求次数,可将多个图表数据合并到一个接口返回。
// api/dashboard_all.php
$data = [
'cpu' => ['value' => 75, 'status' => 'warning'],
'memory' => ['value' => 60, 'status' => 'normal'],
'users' => ['value' => 3200, 'prev' => 2800], // 当前和昨日对比
'realtime' => [
['time' => '10:00:00', 'value' => 50],
['time' => '10:00:05', 'value' => 55],
// ... 最近100条
]
];
echo json_encode($data);
前端大屏 HTML 实现
使用 HTML + ECharts + 定时刷新 PHP 接口。
<!DOCTYPE html>
<html>
<head>监控大屏</title>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<style>
body { background: #0a1f3d; color: white; margin: 0; }
.grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px; padding: 20px; height: 100vh; }
.panel { background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; }
</style>
</head>
<body>
<div class="grid">
<div id="chartCpu" class="panel"></div>
<div id="chartMemory" class="panel"></div>
<div id="chartUsers" class="panel"></div>
<div id="chartTrend" class="panel" style="grid-column: span 3; height: 400px;"></div>
</div>
<script>
// 初始化所有图表
const chartCpu = echarts.init(document.getElementById('chartCpu'));
const chartUsers = echarts.init(document.getElementById('chartUsers'));
const chartTrend = echarts.init(document.getElementById('chartTrend'));
// 定义更新函数
function refreshDashboard() {
fetch('/api/dashboard_all.php')
.then(res => res.json())
.then(data => {
// 1. 更新 CPU 仪表盘 (Gauge)
chartCpu.setOption({
series: [{
type: 'gauge',
startAngle: 180, endAngle: 0,
min: 0, max: 100,
data: [{ value: data.cpu.value, name: 'CPU' }],
axisLine: { lineStyle: { color: [[0.5, '#67e0e3'],[0.8, '#37a2da'],[1, '#fd666d']] } }
}]
});
// 2. 更新用户数 (数字展示 + 对比)
const userDiff = ((data.users.value - data.users.prev) / data.users.prev * 100).toFixed(2);
chartUsers.setOption({
title: { text: `当前用户\n${data.users.value}`, left: 'center', top: 'center', textStyle: {fontSize: 36, color: '#eee'} },
// 也可以使用 ECharts 的 value cards 或 text 组件
graphic: [{
type: 'text', left: 'center', top: '75%',
style: { text: `较昨日: ${userDiff > 0 ? '+' : ''}${userDiff}%`, fill: userDiff > 0 ? '#f00' : '#0f0', fontSize: 16 }
}]
});
// 3. 更新实时趋势折线图
chartTrend.setOption({
xAxis: { type: 'category', data: data.realtime.map(i => i.time) },
yAxis: { type: 'value' },
series: [{ type: 'line', data: data.realtime.map(i => i.value), smooth: true, areaStyle: {} }]
});
})
.catch(console.error);
}
// 首次加载
refreshDashboard();
// 定时刷新: 每3秒请求一次
setInterval(refreshDashboard, 3000);
// 窗口自适应
window.addEventListener('resize', () => {
chartCpu.resize();
chartUsers.resize();
chartTrend.resize();
});
</script>
</body>
</html>
针对“性能与实时性”的优化
如果你希望更实时、服务器压力更小,有两种升级方案:
方案 A:PHP + SSE (Server-Sent Events)
PHP 脚本保持长连接,每 N 秒写一次数据给前端,优点:前端只需一个连接,后端主动推送。
// sse.php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
while (true) {
$data = getLatestFromRedis(); // 获取最新数据
echo "data: " . json_encode($data) . "\n\n";
ob_flush(); flush();
sleep(2); // 推2秒
}
前端使用 EventSource 接收。
方案 B:PHP + Swoole/Workerman
使用 PHP 扩展启动 WebSocket 服务端,后端采集数据后直接推送给前端。 这是一个真正的长连接方案,但需要熟悉 Swoole 库。
| 需求 | 实现方式 | 适用场景 |
|---|---|---|
| 简单、快速 | Ajax 轮询 (每3秒请求PHP接口) | 中小型项目、内部监控、数据吞吐量不大 |
| 较实时、省请求 | SSE (PHP单线程推送) | 数据更新频率稳定、不想使用 WebSocket |
| 高并发、低延迟 | WebSocket (Swoole/Workerman) | 大规模监控、数据每秒变化 |
针对 PHP 项目最稳妥的建议:
保持 PHP 只做数据提供方(接口),前端使用 Ajax 轮询实现大屏动态刷新,如果未来性能不够,再升级为 SSE 或 Swoole。
这个方案成本最低、易维护、兼容性好,也是目前 PHP 生态(如 Laravel、ThinkPHP)最常用的方法。