PHP 心跳检测机制

wen PHP项目 2

本文目录导读:

PHP 心跳检测机制

  1. WebSocket 场景下的心跳检测(最常用)
  2. 长轮询场景的心跳检测
  3. MySQL 数据库连接的心跳检测
  4. Socket 服务端心跳检测(非 WebSocket)
  5. 健康检查接口(服务监控)
  6. 最佳实践建议

在 PHP 开发中,心跳检测(Heartbeat) 是一种用于检测客户端连接状态、保持会话活跃或监控服务健康状态的机制,根据应用场景的不同,PHP 心跳检测有以下几种常见的实现方式:


WebSocket 场景下的心跳检测(最常用)

WebSocket 连接本身不会自动断开,需要心跳包(Ping/Pong) 来保持连接并检测死链。

实现原理

  • 客户端定时发送 ping 数据帧
  • 服务端返回 pong 响应
  • 如果服务端超过 N 秒未收到 ping,则视为连接失效,主动断开

代码示例(使用 Workerman 框架)

use Workerman\Worker;
use Workerman\Timer;
$worker = new Worker('websocket://0.0.0.0:2346');
$worker->onConnect = function ($connection) {
    // 设置连接最后心跳时间
    $connection->lastPingTime = time();
    // 为该连接设置心跳定时器,每 30 秒检查一次
    $connection->heartbeatTimer = Timer::add(30, function () use ($connection) {
        // 如果超过 90 秒没有收到心跳,判定为死连接
        if (time() - $connection->lastPingTime > 90) {
            echo "连接超时,关闭连接\n";
            $connection->close();
        }
    });
};
$worker->onMessage = function ($connection, $data) {
    // 收到任何消息都更新最后心跳时间
    $connection->lastPingTime = time();
    if ($data === 'ping') {
        $connection->send('pong');
    } else {
        echo "收到数据: $data\n";
    }
};
$worker->onClose = function ($connection) {
    // 清理定时器
    if (isset($connection->heartbeatTimer)) {
        Timer::del($connection->heartbeatTimer);
    }
};
Worker::runAll();

前端 JavaScript 客户端示例

// 建立 WebSocket 连接
const ws = new WebSocket('ws://localhost:2346');
// 每 30 秒发送一次心跳
const heartbeat = setInterval(() => {
    if (ws.readyState === WebSocket.OPEN) {
        ws.send('ping');
    }
}, 30000);
ws.onmessage = (event) => {
    if (event.data === 'pong') {
        console.log('心跳响应正常');
    }
};
ws.onclose = () => {
    clearInterval(heartbeat);
    console.log('连接断开');
    // 自动重连逻辑
};

长轮询场景的心跳检测

如果使用的是传统的 HTTP 长轮询,通常配合定时检查实现:

<?php
// 心跳检测轮询接口 heartbeat.php
session_start();
header('Content-Type: application/json');
$max_idle_time = 60; // 最大空闲时间 60 秒
$_SESSION['last_heartbeat'] = time();
// 检查上一次心跳时间
if (isset($_SESSION['last_activity'])) {
    $time_diff = time() - $_SESSION['last_activity'];
    if ($time_diff > $max_idle_time) {
        // 连接已超时
        echo json_encode(['status' => 'timeout', 'message' => '连接已断开']);
        exit;
    }
}
$_SESSION['last_activity'] = time();
echo json_encode(['status' => 'active', 'message' => '心跳正常']);

MySQL 数据库连接的心跳检测

用于检测数据库连接是否仍然有效,避免使用已断开的连接:

<?php
class DBHeartbeat {
    private $pdo;
    private $lastHeartbeat;
    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
        $this->lastHeartbeat = time();
    }
    public function checkConnection() {
        // 如果超过 60 秒未检查,重新验证连接
        if (time() - $this->lastHeartbeat > 60) {
            try {
                // 执行一个轻量查询来检测连接
                $this->pdo->query('SELECT 1');
                $this->lastHeartbeat = time();
                return true;
            } catch (PDOException $e) {
                // 连接已断开,尝试重连
                return $this->reconnect();
            }
        }
        return true;
    }
    private function reconnect() {
        // 重建数据库连接
        $this->pdo = new PDO(
            'mysql:host=localhost;dbname=test',
            'username',
            'password'
        );
        return true;
    }
}

Socket 服务端心跳检测(非 WebSocket)

对于使用 stream_socket_server 的原生 Socket 服务:

<?php
$server = stream_socket_server("tcp://0.0.0.0:9000", $errno, $errstr);
$clients = [];
$lastHeartbeat = [];
while (true) {
    $read = array_merge([$server], $clients);
    $write = null;
    $except = null;
    if (stream_select($read, $write, $except, 1) > 0) {
        foreach ($read as $client) {
            if ($client === $server) {
                // 新连接
                $newClient = stream_socket_accept($server);
                $clients[] = $newClient;
                $lastHeartbeat[(int)$newClient] = time();
            } else {
                $data = fread($client, 1024);
                if ($data === 'ping') {
                    $lastHeartbeat[(int)$client] = time();
                    fwrite($client, 'pong');
                }
            }
        }
    }
    // 检查心跳超时
    $currentTime = time();
    foreach ($clients as $index => $client) {
        if ($currentTime - $lastHeartbeat[(int)$client] > 90) {
            // 断开死连接
            fclose($client);
            unset($clients[$index]);
            unset($lastHeartbeat[(int)$client]);
        }
    }
}

健康检查接口(服务监控)

用于 Kubernetes、Docker 等容器编排系统的健康检查:

<?php
// health.php - 健康检查接口
header('Content-Type: application/json');
$response = [
    'status' => 'ok',
    'timestamp' => time(),
    'uptime' => time() - $_SERVER['REQUEST_TIME'],
];
// 检查数据库连接
try {
    $pdo = new PDO('mysql:host=db;dbname=app', 'user', 'pass');
    $pdo->query('SELECT 1');
    $response['database'] = 'connected';
} catch (PDOException $e) {
    $response['status'] = 'error';
    $response['database'] = 'disconnected';
    http_response_code(500);
}
echo json_encode($response);

最佳实践建议

策略 说明
心跳间隔 建议 30-60 秒,根据网络环境和业务需求调整
超时阈值 通常为心跳间隔的 3 倍,防止网络抖动误判
重连机制 客户端检测到断开后自动重连,设置重试策略
日志记录 记录心跳失败的时间点,便于排查问题
资源清理 断开连接后及时释放资源(定时器、内存、文件句柄)

PHP 心跳检测的核心思想是在固定时间间隔内检查连接或服务是否仍然活跃,若超过阈值未响应则判定为失效,实际项目中最常用的是 WebSocket 心跳机制,配合定时器和 PING/PONG 交互实现稳定的长连接管理,根据具体场景,可以选择最合适的心跳实现方式。

抱歉,评论功能暂时关闭!