PHP 实时显示"场上谁更占优势"方案
这是一个典型的实时态势感知需求,下面给你一套完整、可落地的 PHP 技术方案。

核心思路
数据采集 → 计算优势值 → 实时推送 → 前端展示
- 数据采集:从业务数据源(数据库/API/消息队列)读取双方实时指标
- 优势计算:定义量化模型,输出 -100 ~ +100 的优势分数
- 实时推送:PHP 不适合长连接,推荐 SSE 或 WebSocket
- 前端展示:优势条、仪表盘、趋势图
优势分计算模型
先定义"优势"的量化公式,以对战/竞技场景为例:
<?php
class AdvantageCalculator
{
/**
* 计算优势分:正数=A方占优,负数=B方占优,范围 -100~100
*/
public function calculate(array $stats): float
{
// 各项指标的权重(可按业务调整)
$weights = [
'score' => 0.35, // 比分差
'hp' => 0.25, // 剩余血量/资源
'economy' => 0.20, // 经济/资源
'map' => 0.10, // 地图/位置控制
'momentum' => 0.10, // 近期势头
];
$advantage = 0;
foreach ($weights as $key => $w) {
$a = $stats['a'][$key] ?? 0;
$b = $stats['b'][$key] ?? 0;
$total = $a + $b;
if ($total <= 0) continue;
// 归一化到 -1 ~ 1
$normalized = ($a - $b) / $total;
$advantage += $normalized * $w * 100;
}
return round(max(-100, min(100, $advantage)), 2);
}
}
关键点:优势模型的权重和指标要贴合你的业务,游戏用血量/经济,电商用转化率/流量,体育用得分/控球率。
实时推送:三种方案对比
| 方案 | 优点 | 缺点 | 适用 |
|---|---|---|---|
| 轮询 | 实现最简单 | 延迟高、浪费请求 | 低频更新 |
| SSE ⭐ | PHP 原生支持、单向推送够用、无需额外服务 | 占 PHP-FPM 进程 | 推荐 |
| WebSocket | 全双工、性能好 | 需 Swoole/Workerman | 高频+双向 |
推荐方案:SSE(Server-Sent Events)
PHP 用 SSE 最简单,不依赖 Swoole,浏览器原生 EventSource 支持。
<?php
// stream.php —— SSE 推送端点
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no'); // 关闭 Nginx 缓冲
// 防止脚本超时
set_time_limit(0);
// 断开客户端时自动退出
ignore_user_abort(false);
$calculator = new AdvantageCalculator();
$lastSend = 0;
while (!connection_aborted()) {
// 限制刷新频率,比如每 1 秒推一次(避免刷爆)
if (microtime(true) - $lastSend < 1) {
usleep(200000);
continue;
}
$lastSend = microtime(true);
// 1. 读取最新数据(从 Redis 缓存拿,别每次查库)
$stats = getLatestStats();
// 2. 计算优势
$advantage = $calculator->calculate($stats);
// 3. 推送
$payload = json_encode([
'advantage' => $advantage,
'a' => $stats['a'],
'b' => $stats['b'],
'ts' => time(),
]);
echo "data: {$payload}\n\n";
ob_flush();
flush();
}
// 数据源:优先走 Redis,避免每秒查库
function getLatestStats(): array
{
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$raw = $redis->get('battle:stats:latest');
return $raw ? json_decode($raw, true) : ['a' => [], 'b' => []];
}
生产环境要处理:
- 数据写入方(游戏服务器/采集脚本)把最新指标写进 Redis
- 每个 SSE 连接占一个 PHP-FPM 进程,需调大
pm.max_children - Nginx 需
proxy_buffering off;和proxy_read_timeout调大
前端展示
<!DOCTYPE html>
<html>
<head>
<style>
.bar { width: 600px; height: 40px; background: #eee; border-radius: 20px;
position: relative; overflow: hidden; }
.fill { height: 100%; transition: width .5s ease; }
.a-side { background: linear-gradient(90deg,#3b82f6,#60a5fa); float: left; }
.b-side { background: linear-gradient(90deg,#f87171,#ef4444); float: right; }
.label { position: absolute; top: 8px; width: 100%; text-align: center;
color: #fff; font-weight: bold; }
</style>
</head>
<body>
<div class="bar">
<div class="fill a-side" id="aFill" style="width:50%"></div>
<div class="fill b-side" id="bFill" style="width:50%"></div>
<div class="label" id="label">势均力敌</div>
</div>
<script>
const es = new EventSource('/stream.php');
es.onmessage = (e) => {
const d = JSON.parse(e.data);
// advantage: -100 ~ 100,映射为 A 方宽度 0~100%
const aPercent = (d.advantage + 100) / 2;
document.getElementById('aFill').style.width = aPercent + '%';
document.getElementById('bFill').style.width = (100 - aPercent) + '%';
const adv = d.advantage;
let text = '势均力敌';
if (adv > 30) text = `A方大幅领先 (+${adv})`;
else if (adv > 10) text = `A方略占优势 (+${adv})`;
else if (adv < -30)text = `B方大幅领先 (${adv})`;
else if (adv < -10)text = `B方略占优势 (${adv})`;
document.getElementById('label').textContent = text;
};
es.onerror = () => console.warn('SSE 断开,浏览器会自动重连');
</script>
</body>
</html>
生产级架构建议
[数据源] [缓存] [推送层] [前端]
游戏服务器/埋点 ──写入──▶ Redis (最新快照) ──读取──▶ PHP SSE (stream.php) ──▶ EventSource
│
└─ 保留近 N 秒窗口,用于"势头"计算
关键优化点:
- 数据分层:原始事件 → 聚合快照(Redis)→ 推送,SSE 只读快照,绝不查主库
- 计算下沉:把优势分在写入时就算好存 Redis,SSE 只做转发(省钱省 CPU)
- 多进程注意:SSE 常驻会耗尽 FPM,量大时用 Swoole/Workerman 做常驻服务,或用 Nginx push module
- 降级策略:SSE 不可用时,前端降级为
setInterval轮询/api/advantage - 防抖限流:客户端数据变化频繁时,服务端合并推送(如 500ms 一次)
如果你的场景是"玩家实时对战"
补充几个业务级设计:
- 指标选取:血量、经济、装备、推塔数、控图时长
- "势头"(momentum):用近 10 秒指标变化率的加权,捕捉"正在翻盘"的时刻
- 优势平滑:对原始优势分做 EMA 平滑(
adv = 0.7*adv + 0.3*new),避免数值剧烈跳动 - 阈值告警:
|adv| > 70时可推送"即将锁定胜局"事件,前端加动效
// EMA 平滑示例 $smoothed = $prevAdv * 0.7 + $currentAdv * 0.3;
| 环节 | 推荐做法 |
|---|---|
| 优势模型 | 多指标加权归一化,输出 -100~100 |
| 数据缓存 | Redis 存最新快照 |
| 推送 | SSE(PHP 友好)或 Swoole WebSocket(高并发) |
| 前端 | 优势条 + 趋势折线 + 文字状态 |
| 稳定性 | EMA 平滑、轮询降级、限频推送 |
如果你能告诉我具体场景(电竞/体育/电商/游戏后台)和现有技术栈(有没有 Redis、Swoole),我可以给你更贴合的完整代码。
版权声明:本站文章为原创整理,转载请注明出处。