本文目录导读:

我来为您介绍PHP实时数据大屏后端的完整实现方案。
整体架构设计
┌─────────────────────────────────────────────────┐
│ 数据大屏前端(WebSocket客户端) │
└────────────────────┬────────────────────────────┘
│ WebSocket/HTTP
┌────────────────────▼────────────────────────────┐
│ PHP 后端服务(实时推送) │
├────────────────────────────────────────────────┤
│ 1. WebSocket服务 2. RESTful API接口 │
│ 3. 数据聚合服务 4. 缓存服务 │
│ 5. 定时任务服务 6. 消息队列 │
└────────────────────────────────────────────────┘
核心功能实现
WebSocket 服务(使用 Workerman)
<?php
use Workerman\Worker;
use Workerman\Timer;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
class RealtimeDataServer {
private $worker;
private $dataProvider;
private $clients = [];
public function __construct() {
$this->worker = new Worker('websocket://0.0.0.0:2345');
$this->worker->count = 4;
$this->initEventHandlers();
$this->initDataProvider();
}
private function initEventHandlers() {
$this->worker->onConnect = function(TcpConnection $connection) {
echo "Client connected: {$connection->getRemoteIp()}\n";
};
$this->worker->onMessage = function(TcpConnection $connection, $data) {
$message = json_decode($data, true);
switch ($message['type'] ?? '') {
case 'subscribe':
$this->handleSubscribe($connection, $message);
break;
case 'unsubscribe':
$this->handleUnsubscribe($connection, $message);
break;
case 'ping':
$connection->send(json_encode(['type' => 'pong']));
break;
}
};
$this->worker->onClose = function(TcpConnection $connection) {
echo "Client disconnected\n";
$this->removeClient($connection);
};
}
private function initDataProvider() {
// 每5秒推送一次实时数据
Timer::add(5, function() {
$this->broadcastData();
});
// 每分钟推送统计数据
Timer::add(60, function() {
$this->broadcastStatistics();
});
}
private function handleSubscribe($connection, $message) {
$channel = $message['channel'] ?? 'default';
$clientId = spl_object_hash($connection);
$this->clients[$clientId] = [
'connection' => $connection,
'channel' => $channel
];
// 发送初始数据
$initialData = $this->getChannelData($channel);
$connection->send(json_encode([
'type' => 'initial_data',
'channel' => $channel,
'data' => $initialData
]));
}
private function handleUnsubscribe($connection, $message) {
$this->removeClient($connection);
}
private function removeClient($connection) {
$clientId = spl_object_hash($connection);
unset($this->clients[$clientId]);
}
private function broadcastData() {
$data = $this->getRealtimeData();
foreach ($this->clients as $client) {
$client['connection']->send(json_encode([
'type' => 'realtime_data',
'data' => $data,
'timestamp' => time()
]));
}
}
private function broadcastStatistics() {
$stats = $this->getStatistics();
foreach ($this->clients as $client) {
$client['connection']->send(json_encode([
'type' => 'statistics',
'data' => $stats,
'timestamp' => time()
]));
}
}
private function getChannelData($channel) {
// 根据订阅频道返回对应数据
switch ($channel) {
case 'orders':
return $this->getOrdersData();
case 'users':
return $this->getUsersData();
default:
return $this->getRealtimeData();
}
}
private function getRealtimeData() {
// 模拟实时数据,实际项目中从数据库/缓存获取
return [
'online_users' => rand(1000, 5000),
'new_orders' => rand(50, 200),
'sales_amount' => rand(10000, 50000),
'visitors' => rand(500, 2000),
'conversion_rate' => rand(1, 10) / 10,
'regions' => $this->getRegionData()
];
}
private function getStatistics() {
return [
'total_users' => rand(50000, 100000),
'total_orders' => rand(10000, 50000),
'total_sales' => rand(1000000, 5000000),
'avg_order_value' => rand(50, 200),
'hourly_stats' => $this->getHourlyStats()
];
}
private function getRegionData() {
$regions = ['北京', '上海', '广州', '深圳', '杭州', '成都'];
$data = [];
foreach ($regions as $region) {
$data[] = [
'name' => $region,
'value' => rand(100, 1000)
];
}
return $data;
}
private function getHourlyStats() {
$hours = [];
for ($i = 0; $i < 24; $i++) {
$hours[] = [
'hour' => $i,
'orders' => rand(10, 100),
'sales' => rand(100, 1000)
];
}
return $hours;
}
}
// 启动服务
$server = new RealtimeDataServer();
Worker::runAll();
RESTful API 接口
<?php
use Slim\Factory\AppFactory;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
require __DIR__ . '/vendor/autoload.php';
class DashboardAPI {
private $app;
private $cache;
private $db;
public function __construct() {
$this->app = AppFactory::create();
$this->initDatabase();
$this->initRoutes();
}
private function initDatabase() {
// 使用PDO连接数据库
$this->db = new PDO(
'mysql:host=localhost;dbname=dashboard;charset=utf8mb4',
'username',
'password'
);
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 初始化Redis缓存
$this->cache = new Redis();
$this->cache->connect('localhost', 6379);
}
private function initRoutes() {
$this->app->get('/api/dashboard/summary', function(Request $request, Response $response) {
$data = $this->getSummaryData();
return $this->jsonResponse($response, $data);
});
$this->app->get('/api/dashboard/realtime', function(Request $request, Response $response) {
$data = $this->getRealtimeData();
return $this->jsonResponse($response, $data);
});
$this->app->get('/api/dashboard/statistics', function(Request $request, Response $response) {
$params = $request->getQueryParams();
$startDate = $params['start_date'] ?? date('Y-m-d', strtotime('-7 days'));
$endDate = $params['end_date'] ?? date('Y-m-d');
$data = $this->getStatistics($startDate, $endDate);
return $this->jsonResponse($response, $data);
});
$this->app->get('/api/dashboard/{metric}', function(Request $request, Response $response, $args) {
$metric = $args['metric'];
$data = $this->getMetricData($metric);
return $this->jsonResponse($response, $data);
});
}
private function getSummaryData() {
// 检查缓存
$cacheKey = 'dashboard:summary';
$cached = $this->cache->get($cacheKey);
if ($cached !== false) {
return json_decode($cached, true);
}
// 从数据库获取数据
$today = date('Y-m-d');
$sql = "SELECT
(SELECT COUNT(*) FROM users WHERE created_at >= ?) as new_users,
(SELECT COUNT(*) FROM orders WHERE created_at >= ?) as today_orders,
(SELECT SUM(total_amount) FROM orders WHERE created_at >= ?) as today_sales,
(SELECT COUNT(*) FROM orders) as total_orders,
(SELECT SUM(total_amount) FROM orders) as total_sales";
$stmt = $this->db->prepare($sql);
$stmt->execute([$today, $today, $today]);
$data = $stmt->fetch(PDO::FETCH_ASSOC);
// 缓存数据(5分钟)
$this->cache->setex($cacheKey, 300, json_encode($data));
return $data;
}
private function getRealtimeData() {
// 实时数据直接从缓存获取(由WebSocket服务更新)
$cacheKey = 'dashboard:realtime';
$cached = $this->cache->get($cacheKey);
if ($cached !== false) {
return json_decode($cached, true);
}
return $this->getFallbackRealtimeData();
}
private function getStatistics($startDate, $endDate) {
$cacheKey = "dashboard:stats:{$startDate}:{$endDate}";
$cached = $this->cache->get($cacheKey);
if ($cached !== false) {
return json_decode($cached, true);
}
// 按天统计数据
$sql = "SELECT
DATE(created_at) as date,
COUNT(*) as orders,
SUM(total_amount) as sales,
COUNT(DISTINCT user_id) as users
FROM orders
WHERE created_at BETWEEN ? AND ?
GROUP BY DATE(created_at)
ORDER BY date";
$stmt = $this->db->prepare($sql);
$stmt->execute([$startDate, $endDate . ' 23:59:59']);
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 缓存数据(1小时)
$this->cache->setex($cacheKey, 3600, json_encode($data));
return $data;
}
private function getMetricData($metric) {
// 根据不同指标返回不同数据
switch ($metric) {
case 'sales':
$data = $this->getSalesMetrics();
break;
case 'users':
$data = $this->getUserMetrics();
break;
case 'system':
$data = $this->getSystemMetrics();
break;
default:
$data = ['status' => 'error', 'message' => 'Unknown metric'];
}
return $data;
}
private function getSalesMetrics() {
return [
'today' => [
'amount' => $this->getTodaySales(),
'orders' => $this->getTodayOrders()
],
'weekly' => $this->getWeeklySales(),
'monthly' => $this->getMonthlySales()
];
}
private function getUserMetrics() {
return [
'active_today' => $this->getActiveUsersToday(),
'active_online' => $this->getOnlineUsers(),
'growth_rate' => $this->getUserGrowthRate()
];
}
private function getSystemMetrics() {
return [
'cpu_usage' => sys_getloadavg()[0],
'memory_usage' => memory_get_usage(true) / 1024 / 1024,
'db_connections' => $this->db->getAttribute(PDO::ATTR_CONNECTION_STATUS)
];
}
private function getTodaySales() {
$stmt = $this->db->query("SELECT SUM(total_amount) FROM orders WHERE DATE(created_at) = CURDATE()");
return (float)$stmt->fetchColumn();
}
private function getTodayOrders() {
$stmt = $this->db->query("SELECT COUNT(*) FROM orders WHERE DATE(created_at) = CURDATE()");
return (int)$stmt->fetchColumn();
}
private function getWeeklySales() {
$stmt = $this->db->query("SELECT DAYOFWEEK(created_at) as day, SUM(total_amount) as sales FROM orders WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) GROUP BY DAYOFWEEK(created_at)");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
private function getMonthlySales() {
$stmt = $this->db->query("SELECT DATE_FORMAT(created_at, '%Y-%m') as month, SUM(total_amount) as sales FROM orders WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH) GROUP BY DATE_FORMAT(created_at, '%Y-%m')");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
private function getActiveUsersToday() {
$stmt = $this->db->query("SELECT COUNT(DISTINCT user_id) FROM user_activity WHERE DATE(created_at) = CURDATE()");
return (int)$stmt->fetchColumn();
}
private function getOnlineUsers() {
$cacheKey = 'dashboard:online_users';
$online = $this->cache->get($cacheKey);
return $online !== false ? (int)$online : 0;
}
private function getUserGrowthRate() {
$today = $this->getUserCountToday();
$monthAgo = $this->getUserCountMonthAgo();
if ($monthAgo > 0) {
return (($today - $monthAgo) / $monthAgo) * 100;
}
return 0;
}
private function getUserCountToday() {
$stmt = $this->db->query("SELECT COUNT(*) FROM users WHERE DATE(created_at) = CURDATE()");
return (int)$stmt->fetchColumn();
}
private function getUserCountMonthAgo() {
$stmt = $this->db->query("SELECT COUNT(*) FROM users WHERE DATE(created_at) = DATE_SUB(CURDATE(), INTERVAL 30 DAY)");
return (int)$stmt->fetchColumn();
}
private function getFallbackRealtimeData() {
// 从数据库获取最近的实时数据
$data = [
'online_users' => 0,
'new_orders' => 0,
'sales_amount' => 0,
'visitors' => 0,
'timestamp' => time(),
'source' => 'fallback'
];
try {
$stmt = $this->db->query("
SELECT
(SELECT COUNT(*) FROM active_sessions WHERE online = 1) as online_users,
(SELECT COUNT(*) FROM orders WHERE created_at >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)) as new_orders,
(SELECT SUM(total_amount) FROM orders WHERE created_at >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)) as sales_amount,
(SELECT COUNT(*) FROM visitors WHERE last_seen >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)) as visitors
");
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if ($result) {
$data = array_merge($data, $result);
}
} catch (Exception $e) {
// 记录错误日志
error_log("Error getting fallback data: " . $e->getMessage());
}
return $data;
}
private function jsonResponse(Response $response, $data) {
$response->getBody()->write(json_encode([
'status' => 'success',
'data' => $data
]));
return $response->withHeader('Content-Type', 'application/json');
}
public function run() {
$this->app->run();
}
}
// 启动API服务
$api = new DashboardAPI();
$api->run();
数据提供者(数据源)
<?php
class DataProvider {
private $db;
private $cache;
private $messageQueue;
public function __construct($db, $cache, $messageQueue) {
$this->db = $db;
$this->cache = $cache;
$this->messageQueue = $messageQueue;
}
public function updateRealtimeData() {
// 更新实时数据到缓存
$data = [
'online_users' => $this->getOnlineUsers(),
'new_orders' => $this->getRecentOrders(),
'sales_amount' => $this->getRecentSales(),
'visitors' => $this->getRecentVisitors(),
'timestamp' => time()
];
$this->cache->setex('dashboard:realtime', 5, json_encode($data));
// 发布到消息队列
$this->messageQueue->publish('dashboard', 'realtime', $data);
}
private function getOnlineUsers() {
$stmt = $this->db->query("SELECT COUNT(*) FROM active_sessions WHERE last_activity > DATE_SUB(NOW(), INTERVAL 5 MINUTE)");
return (int)$stmt->fetchColumn();
}
private function getRecentOrders() {
$stmt = $this->db->query("SELECT COUNT(*) FROM orders WHERE created_at > DATE_SUB(NOW(), INTERVAL 5 MINUTE)");
return (int)$stmt->fetchColumn();
}
private function getRecentSales() {
$stmt = $this->db->query("SELECT COALESCE(SUM(total_amount), 0) FROM orders WHERE created_at > DATE_SUB(NOW(), INTERVAL 5 MINUTE)");
return (float)$stmt->fetchColumn();
}
private function getRecentVisitors() {
$stmt = $this->db->query("SELECT COUNT(*) FROM visitors WHERE last_seen > DATE_SUB(NOW(), INTERVAL 5 MINUTE)");
return (int)$stmt->fetchColumn();
}
public function startUpdater() {
// 使用定时任务更新数据
while (true) {
$this->updateRealtimeData();
sleep(5); // 每5秒更新一次
}
}
}
消息队列配置(使用Redis)
<?php
use Predis\Client;
class MessageQueue {
private $redis;
private $subscriptions;
public function __construct(Client $redis) {
$this->redis = $redis;
$this->subscriptions = [];
}
public function publish($channel, $event, $data) {
$message = json_encode([
'event' => $event,
'data' => $data,
'timestamp' => time()
]);
$this->redis->publish($channel, $message);
}
public function subscribe($channel, callable $callback) {
$this->subscriptions[$channel][] = $callback;
}
public function listen() {
$loop = new \React\EventLoop\StreamSelectLoop();
$client = new \React\Socket\SocketClient('127.0.0.1:6379');
$this->redis->psubscribe(['dashboard_*'], function($redis, $pattern, $channel, $message) {
foreach ($this->subscriptions[$channel] ?? [] as $callback) {
$callback(json_decode($message, true));
}
});
}
}
数据库表结构设计
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP NULL
);
-- 订单表
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
paid_at TIMESTAMP NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 访问者表
CREATE TABLE visitors (
id INT PRIMARY KEY AUTO_INCREMENT,
session_id VARCHAR(64) NOT NULL UNIQUE,
ip_address VARCHAR(45),
page_url VARCHAR(255),
visited_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_seen TIMESTAMP NULL
);
-- 实时统计数据表
CREATE TABLE realtime_stats (
id INT PRIMARY KEY AUTO_INCREMENT,
metric_name VARCHAR(50) NOT NULL,
metric_value DECIMAL(15,2) NOT NULL,
recorded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_metric_name_time (metric_name, recorded_at)
);
-- 用户活跃记录表
CREATE TABLE user_activity (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
action VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
部署与配置
启动脚本
<?php // start_server.php - 主服务启动脚本 require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; // 启动WebSocket服务 $realtimeServer = new RealtimeDataServer(); Worker::runAll(); // 启动RESTful API $api = new DashboardAPI();
Nginx 配置
server {
listen 80;
server_name dashboard.example.com;
# RESTful API
location /api {
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
}
# WebSocket
location /ws {
proxy_pass http://127.0.0.1:2345;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
}
# 静态文件
location / {
root /var/www/dashboard/frontend;
index index.html;
}
}
Docker 部署配置
# Dockerfile
FROM php:8.0-apache
RUN apt-get update && apt-get install -y \
libpq-dev \
redis-server \
supervisor \
&& docker-php-ext-install pdo pdo_mysql \
&& docker-php-ext-enable pdo pdo_mysql
WORKDIR /var/www/html
COPY . /var/www/html
RUN curl -sS https://getcomposer.org/installer | php \
&& mv composer.phar /usr/local/bin/composer \
&& composer install --no-dev
# 安装 Workerman
RUN composer require workerman/workerman \
&& composer require slim/slim:^4.0
# 启动脚本
COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh
EXPOSE 80 2345 6379
CMD ["/docker-entrypoint.sh"]
# docker-compose.yml
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: dashboard
MYSQL_USER: dashboard
MYSQL_PASSWORD: dashboard123
volumes:
- db_data:/var/lib/mysql
ports:
- "3306:3306"
redis:
image: redis:alpine
ports:
- "6379:6379"
app:
build: .
ports:
- "80:80"
- "2345:2345"
environment:
DB_HOST: db
DB_NAME: dashboard
DB_USER: dashboard
DB_PASS: dashboard123
REDIS_HOST: redis
CACHE_TTL: 300
depends_on:
- db
- redis
volumes:
- ./:/var/www/html
restart: unless-stopped
workers:
build: .
command: php websocket_server.php
environment:
DB_HOST: db
REDIS_HOST: redis
depends_on:
- app
- db
- redis
volumes:
- ./:/var/www/html
restart: unless-stopped
volumes:
db_data:
性能优化建议
- 使用Redis/APCu缓存减少数据库查询
- 数据库索引优化 - 定期运行EXPLAIN分析查询
- 启用OPcache提高PHP执行效率
- 使用CDN缓存静态资源
- 数据库读写分离 - 主库负责写入,从库负责读取
- 使用连接池管理数据库连接
- 消息队列解耦数据产生和处理逻辑
监控与日志
<?php
class DashboardLogger {
private $logDir;
private $file;
public function __construct($logDir = '/var/log/dashboard') {
$this->logDir = $logDir;
if (!is_dir($logDir)) {
mkdir($logDir, 0777, true);
}
$this->file = $logDir . '/dashboard_' . date('Y-m-d') . '.log';
}
public function log($message, $level = 'INFO') {
$logEntry = sprintf(
"[%s] [%s] %s\n",
date('Y-m-d H:i:s'),
$level,
$message
);
file_put_contents($this->file, $logEntry, FILE_APPEND);
}
public function error($message) {
$this->log($message, 'ERROR');
}
public function warning($message) {
$this->log($message, 'WARNING');
}
public function info($message) {
$this->log($message, 'INFO');
}
}
这个完整的PHP实时数据大屏后端方案提供了:
- 实时数据推送(WebSocket)
- RESTful API
- 数据缓存
- 消息队列
- 数据库设计
- 部署配置
- 日志监控
您可以根据实际需求调整数据源、推送频率和接口设计。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播