PHP项目怎么实现探针接口?

wen java案例 1

本文目录导读:

PHP项目怎么实现探针接口?

  1. 基础健康检查探针
  2. 完整的环境诊断探针
  3. 安全增强的探针
  4. 中间件式探针(适用于框架)
  5. 使用现成的探针库
  6. Nginx配置示例
  7. 最佳实践建议

在PHP项目中实现探针接口(通常用于健康检查、性能监控或环境诊断),主要有以下几种常用方法和最佳实践。

基础健康检查探针

最简单的探针,用于检测服务是否存活。

// health.php
<?php
header('Content-Type: application/json');
$status = [
    'status' => 'ok',
    'timestamp' => time(),
    'service' => 'my-php-app'
];
echo json_encode($status);

完整的环境诊断探针

包含系统信息、PHP配置、数据库连接等详细检测。

// probe.php
<?php
class Probe {
    private $checks = [];
    private $status = 'ok';
    public function run() {
        $this->checkPHPVersion();
        $this->checkExtensions();
        $this->checkDatabase();
        $this->checkDiskSpace();
        $this->checkMemory();
        $this->checkUptime();
        return $this->generateResponse();
    }
    private function checkPHPVersion() {
        $this->checks['php_version'] = [
            'value' => PHP_VERSION,
            'status' => version_compare(PHP_VERSION, '7.4', '>=') ? 'ok' : 'warning'
        ];
    }
    private function checkExtensions() {
        $required = ['pdo', 'pdo_mysql', 'json', 'mbstring', 'openssl'];
        $missing = [];
        foreach ($required as $ext) {
            if (!extension_loaded($ext)) {
                $missing[] = $ext;
            }
        }
        $this->checks['extensions'] = [
            'required' => $required,
            'missing' => $missing,
            'status' => empty($missing) ? 'ok' : 'error'
        ];
    }
    private function checkDatabase() {
        try {
            $db = new PDO(
                'mysql:host=localhost;dbname=test;charset=utf8mb4',
                'username',
                'password',
                [PDO::ATTR_TIMEOUT => 3]
            );
            $this->checks['database'] = [
                'status' => 'ok',
                'message' => '数据库连接正常'
            ];
        } catch (PDOException $e) {
            $this->checks['database'] = [
                'status' => 'error',
                'message' => '数据库连接失败: ' . $e->getMessage()
            ];
            $this->status = 'error';
        }
    }
    private function checkDiskSpace() {
        $diskFree = disk_free_space('/');
        $diskTotal = disk_total_space('/');
        $usagePercent = round((1 - $diskFree / $diskTotal) * 100, 2);
        $this->checks['disk_space'] = [
            'free' => $this->formatBytes($diskFree),
            'total' => $this->formatBytes($diskTotal),
            'usage_percent' => $usagePercent . '%',
            'status' => $usagePercent < 90 ? 'ok' : 'warning'
        ];
    }
    private function checkMemory() {
        $this->checks['memory'] = [
            'memory_limit' => ini_get('memory_limit'),
            'memory_usage' => $this->formatBytes(memory_get_usage()),
            'peak_memory' => $this->formatBytes(memory_get_peak_usage()),
            'status' => 'ok'
        ];
    }
    private function checkUptime() {
        if (PHP_OS_FAMILY === 'Linux') {
            $uptime = file_get_contents('/proc/uptime');
            $seconds = floatval(explode(' ', $uptime)[0]);
            $this->checks['uptime'] = [
                'value' => $this->formatTime($seconds),
                'status' => 'ok'
            ];
        }
    }
    private function formatBytes($bytes) {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $i = 0;
        while ($bytes >= 1024 && $i < count($units) - 1) {
            $bytes /= 1024;
            $i++;
        }
        return round($bytes, 2) . ' ' . $units[$i];
    }
    private function formatTime($seconds) {
        $days = floor($seconds / 86400);
        $hours = floor(($seconds % 86400) / 3600);
        $minutes = floor(($seconds % 3600) / 60);
        return "{$days}天 {$hours}小时 {$minutes}分钟";
    }
    private function generateResponse() {
        header('Content-Type: application/json');
        return json_encode([
            'status' => $this->status,
            'timestamp' => time(),
            'hostname' => gethostname(),
            'server_ip' => $_SERVER['SERVER_ADDR'] ?? 'unknown',
            'checks' => $this->checks
        ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
    }
}
$probe = new Probe();
echo $probe->run();

安全增强的探针

防止未授权访问。

// secure_probe.php
<?php
class SecureProbe {
    private $apiKey;
    private $allowedIps = [];
    private $rateLimit = [];
    public function __construct() {
        // 从环境变量获取API密钥
        $this->apiKey = $_ENV['PROBE_API_KEY'] ?? getenv('PROBE_API_KEY');
        // 允许的IP白名单
        $this->allowedIps = [
            '127.0.0.1',
            '::1',
            '192.168.1.0/24'
        ];
    }
    public function authenticate() {
        $clientIp = $_SERVER['REMOTE_ADDR'];
        // IP白名单检查
        if (!$this->checkIpWhitelist($clientIp)) {
            $this->denyAccess('IP not allowed');
        }
        // API密钥验证
        $requestKey = $_GET['key'] ?? $_SERVER['HTTP_X_PROBE_KEY'] ?? '';
        if ($this->apiKey && $requestKey !== $this->apiKey) {
            $this->denyAccess('Invalid API key');
        }
        // 速率限制
        if (!$this->checkRateLimit($clientIp)) {
            $this->denyAccess('Rate limit exceeded');
        }
        return true;
    }
    private function checkIpWhitelist($ip) {
        foreach ($this->allowedIps as $allowed) {
            if (strpos($allowed, '/') !== false) {
                // CIDR检查
                if ($this->cidrMatch($ip, $allowed)) {
                    return true;
                }
            } else {
                if ($ip === $allowed) {
                    return true;
                }
            }
        }
        return false;
    }
    private function cidrMatch($ip, $cidr) {
        list($subnet, $mask) = explode('/', $cidr);
        $ipLong = ip2long($ip);
        $subnetLong = ip2long($subnet);
        $maskLong = -1 << (32 - $mask);
        return ($ipLong & $maskLong) === ($subnetLong & $maskLong);
    }
    private function checkRateLimit($ip) {
        $window = 60; // 60秒窗口
        $limit = 10;  // 最多10次请求
        $cacheFile = sys_get_temp_dir() . '/probe_rate_' . md5($ip);
        if (file_exists($cacheFile)) {
            $data = json_decode(file_get_contents($cacheFile), true);
            $currentTime = time();
            // 清理过期记录
            $data = array_filter($data, function($timestamp) use ($currentTime, $window) {
                return $timestamp > ($currentTime - $window);
            });
            if (count($data) >= $limit) {
                return false;
            }
            $data[] = $currentTime;
        } else {
            $data = [time()];
        }
        file_put_contents($cacheFile, json_encode($data));
        return true;
    }
    private function denyAccess($reason) {
        http_response_code(403);
        header('Content-Type: application/json');
        echo json_encode([
            'status' => 'error',
            'message' => 'Access denied: ' . $reason
        ]);
        exit;
    }
}
// 使用示例
$secureProbe = new SecureProbe();
$secureProbe->authenticate();
// ... 继续执行探针逻辑

中间件式探针(适用于框架)

以Laravel为例的路由定义:

// routes/api.php
Route::get('/health', function () {
    return response()->json([
        'status' => 'ok',
        'timestamp' => now()->timestamp
    ]);
});
// 详细探针带认证
Route::middleware(['probe.auth'])->get('/probe', 'ProbeController@index');

对应的中间件:

// app/Http/Middleware/ProbeAuth.php
<?php
namespace App\Http\Middleware;
use Closure;
class ProbeAuth
{
    public function handle($request, Closure $next)
    {
        $apiKey = config('app.probe_api_key');
        if ($apiKey && $request->header('X-Probe-Key') !== $apiKey) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }
        return $next($request);
    }
}

使用现成的探针库

推荐几个常用的PHP探针库:

Composer包安装

composer require abraham/twitteroauth  # 其他用途,仅作示例

常用的专业探针库:

  • php-health-check: composer require php-health-check/health-check
  • laminas/laminas-diagnostics: composer require laminas/laminas-diagnostics
  • enrise/healthcheck: composer require enrise/healthcheck

使用php-health-check示例

// 使用第三方库实现
require 'vendor/autoload.php';
use Laminas\Diagnostics\Check\PhpVersion;
use Laminas\Diagnostics\Runner\Runner;
$runner = new Runner();
$runner->addCheck(new PhpVersion('7.4'));
$results = $runner->run();
$data = [];
foreach ($results as $checkName => $result) {
    $data['checks'][$checkName] = [
        'status' => $result->getStatus() ? 'ok' : 'fail',
        'message' => $result->getDescription()
    ];
}
echo json_encode($data);

Nginx配置示例

保护探针接口不被公网直接访问:

# nginx配置
location /probe {
    # 仅允许内网访问
    allow 127.0.0.1;
    allow 192.168.0.0/16;
    allow 10.0.0.0/8;
    deny all;
    # 或者通过密码保护
    auth_basic "Probe Access";
    auth_basic_user_file /etc/nginx/.htpasswd_probe;
    try_files $uri /index.php?$args;
}

最佳实践建议

  1. 安全性优先

    • 使用API密钥验证
    • IP白名单限制
    • HTTPS传输
    • 速率限制
  2. 不要暴露敏感信息

    • 不要显示数据库密码
    • 不要显示文件系统路径
    • 生产环境限制详情输出
  3. 监控与告警

    • 配置外部监控服务(如Prometheus、New Relic)
    • 设置状态码对应(200=正常,503=异常)
    • 集成日志记录
  4. 性能考虑

    • 探针请求不应影响正常业务
    • 使用缓存减少数据库查询
    • 设置超时时间(如3秒)

根据你的项目规模和需求选择合适的方案,对于小型项目,基础的健康检查就足够;对于生产环境,建议使用安全增强的探针并集成监控系统。

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