本文目录导读:

在 PHP 中实现自定义指标暴露接口,通常用于监控系统(如 Prometheus、Grafana 等),以下是几种常见的实现方案:
Prometheus 格式(最常用)
使用 promphp/prometheus_client_php
<?php
// metrics.php - Prometheus 指标暴露接口
require_once 'vendor/autoload.php';
use Prometheus\CollectorRegistry;
use Prometheus\Storage\InMemory;
use Prometheus\RenderTextFormat;
// 创建注册表
$registry = new CollectorRegistry(new InMemory());
// 定义自定义指标
$counter = $registry->registerCounter(
'app_requests_total',
'Total number of requests',
['method', 'endpoint']
);
$gauge = $registry->registerGauge(
'app_memory_usage_bytes',
'Current memory usage',
['service']
);
$histogram = $registry->registerHistogram(
'app_request_duration_seconds',
'Request duration in seconds',
['method'],
[0.1, 0.5, 1, 2, 5]
);
// 记录指标数据
$counter->incBy(10, ['GET', '/api/users']);
$counter->incBy(5, ['POST', '/api/users']);
$gauge->set(memory_get_usage(true), ['api-service']);
$gauge->set(memory_get_usage(true), ['web-service']);
// 模拟请求耗时数据
$histogram->observe(0.3, ['GET']);
$histogram->observe(1.2, ['POST']);
// 渲染并输出指标
$renderer = new RenderTextFormat();
$result = $renderer->render($registry->getMetricFamilySamples());
header('Content-Type: ' . RenderTextFormat::MIME_TYPE);
echo $result;
轻量级实现(不依赖第三方库)
<?php
// custom_metrics.php
header('Content-Type: text/plain; version=0.0.4');
// 自定义指标数据存储(实际应用中可能从数据库或缓存读取)
$metrics = [
'app_requests_total' => [
'help' => 'Total number of HTTP requests',
'type' => 'counter',
'values' => [
['labels' => ['method="GET"', 'endpoint="/home"'], 'value' => 12345],
['labels' => ['method="POST"', 'endpoint="/api/data"'], 'value' => 6789],
]
],
'app_http_request_duration_seconds' => [
'help' => 'HTTP request duration in seconds',
'type' => 'histogram',
'values' => [
['labels' => ['le="0.1"'], 'value' => 100],
['labels' => ['le="0.5"'], 'value' => 300],
['labels' => ['le="1"'], 'value' => 400],
['labels' => ['le="2"'], 'value' => 450],
['labels' => ['le="+Inf"'], 'value' => 500],
]
]
];
// 输出 Prometheus 格式
foreach ($metrics as $name => $metric) {
echo "# HELP {$name} {$metric['help']}\n";
echo "# TYPE {$name} {$metric['type']}\n";
foreach ($metric['values'] as $data) {
$labels = implode(',', $data['labels']);
echo "{$name}{{$labels}} {$data['value']}\n";
}
echo "\n";
}
JSON 格式(自定义监控系统)
<?php
// json_metrics.php
header('Content-Type: application/json');
class MetricsCollector {
private $metrics = [];
public function increment($name, $labels = [], $value = 1) {
$key = $this->buildKey($name, $labels);
if (!isset($this->metrics[$key])) {
$this->metrics[$key] = [
'name' => $name,
'labels' => $labels,
'value' => 0,
'timestamp' => time()
];
}
$this->metrics[$key]['value'] += $value;
$this->metrics[$key]['timestamp'] = time();
}
public function gauge($name, $value, $labels = []) {
$key = $this->buildKey($name, $labels);
$this->metrics[$key] = [
'name' => $name,
'labels' => $labels,
'value' => $value,
'timestamp' => time()
];
}
public function histogram($name, $value, $labels = []) {
$key = $this->buildKey($name, $labels);
if (!isset($this->metrics[$key])) {
$this->metrics[$key] = [
'name' => $name,
'labels' => $labels,
'count' => 0,
'sum' => 0,
'buckets' => [0.1, 0.5, 1, 2, 5],
'bucket_counts' => array_fill(0, 6, 0)
];
}
$this->metrics[$key]['count']++;
$this->metrics[$key]['sum'] += $value;
foreach ($this->metrics[$key]['buckets'] as $i => $bucket) {
if ($value <= $bucket) {
$this->metrics[$key]['bucket_counts'][$i]++;
}
}
}
private function buildKey($name, $labels) {
return $name . '|' . json_encode($labels);
}
public function render() {
return json_encode([
'timestamp' => time(),
'service' => gethostname(),
'metrics' => $this->metrics
], JSON_PRETTY_PRINT);
}
}
// 使用示例
$collector = new MetricsCollector();
$collector->increment('http_requests', ['endpoint' => '/home', 'method' => 'GET']);
$collector->increment('http_requests', ['endpoint' => '/api/data', 'method' => 'POST'], 5);
$collector->gauge('memory_usage', memory_get_usage(true), ['service' => 'api']);
$collector->histogram('request_duration', 0.3, ['endpoint' => '/home']);
echo $collector->render();
使用框架集成(以 Laravel 为例)
<?php
// routes/api.php
use App\Services\MetricsService;
use Prometheus\CollectorRegistry;
use Prometheus\Storage\Redis;
use Prometheus\RenderTextFormat;
Route::get('/metrics', function () {
// 使用 Redis 存储(适合多实例)
$registry = new CollectorRegistry(new Redis(['host' => 'redis', 'port' => 6379]));
// 或者使用 APCu 存储
// $registry = new CollectorRegistry(new APCng());
$renderer = new RenderTextFormat();
$result = $renderer->render($registry->getMetricFamilySamples());
return response($result)
->header('Content-Type', RenderTextFormat::MIME_TYPE);
});
// app/Services/MetricsService.php
class MetricsService {
private $registry;
public function __construct() {
$this->registry = new CollectorRegistry(new InMemory());
}
public function incrementCounter($name, $labels = []) {
$counter = $this->registry->getOrRegisterCounter(
'app',
$name,
'Application counter: ' . $name,
array_keys($labels)
);
$counter->incBy(1, array_values($labels));
}
}
完整的企业级实现
<?php
// enterprise_metrics.php
require_once 'vendor/autoload.php';
use Prometheus\CollectorRegistry;
use Prometheus\Storage\Redis;
use Prometheus\RenderTextFormat;
use Predis\Client;
class EnterpriseMetrics {
private $registry;
private $appName;
public function __construct($redisConfig, $appName = 'my_app') {
$this->appName = $appName;
// 配置 Redis 存储
$redis = new Client($redisConfig);
$this->registry = new CollectorRegistry(new Redis($redis));
// 注册基本指标
$this->registerBasicMetrics();
}
private function registerBasicMetrics() {
// 系统指标
$this->registry->registerGauge(
'system_cpu_usage_percent',
'CPU usage percentage',
['host', 'app']
);
$this->registry->registerGauge(
'system_memory_usage_bytes',
'Memory usage in bytes',
['host', 'app']
);
// 业务指标
$this->registry->registerCounter(
'business_requests_total',
'Total business requests',
['app', 'endpoint', 'status']
);
$this->registry->registerHistogram(
'business_request_duration_seconds',
'Business request duration',
['endpoint'],
[0.1, 0.5, 1, 2, 5, 10]
);
}
public function updateSystemMetrics() {
$host = gethostname();
// CPU 使用率
$cpu = sys_getloadavg()[0] / 100;
$this->registry->getGauge('system_cpu_usage_percent')
->set($cpu, [$host, $this->appName]);
// 内存使用
$mem = memory_get_usage(true);
$this->registry->getGauge('system_memory_usage_bytes')
->set($mem, [$host, $this->appName]);
}
public function recordRequest($endpoint, $status, $duration) {
$counter = $this->registry->getCounter('business_requests_total');
$counter->incBy(1, [$this->appName, $endpoint, $status]);
$histogram = $this->registry->getHistogram('business_request_duration_seconds');
$histogram->observe($duration, [$endpoint]);
}
public function render() {
$renderer = new RenderTextFormat();
return $renderer->render($this->registry->getMetricFamilySamples());
}
}
// 在入口脚本中使用
$metrics = new EnterpriseMetrics([
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => 6379,
]);
// 更新系统指标
$metrics->updateSystemMetrics();
// 输出指标
header('Content-Type: ' . RenderTextFormat::MIME_TYPE);
echo $metrics->render();
安全防护
<?php
// secure_metrics.php
session_start();
class MetricsSecurity {
private $allowedIPs = ['127.0.0.1', '192.168.1.0/24'];
private $apiKeys = ['your-secure-api-key'];
public function validateAccess() {
// IP 白名单检查
if (!$this->checkIP()) {
http_response_code(403);
die('Forbidden');
}
// API Key 检查
$apiKey = $_SERVER['HTTP_X_API_KEY'] ?? '';
if (!in_array($apiKey, $this->apiKeys)) {
http_response_code(401);
die('Unauthorized');
}
// 速率限制
$this->rateLimit();
return true;
}
private function checkIP() {
$ip = $_SERVER['REMOTE_ADDR'];
foreach ($this->allowedIPs as $allowedIP) {
if (strpos($allowedIP, '/') !== false) {
// CIDR 检查
if ($this->ipInCIDR($ip, $allowedIP)) {
return true;
}
} elseif ($ip === $allowedIP) {
return true;
}
}
return false;
}
private function ipInCIDR($ip, $cidr) {
list($subnet, $bits) = explode('/', $cidr);
$ip_bin = ip2long($ip);
$subnet_bin = ip2long($subnet);
$mask = -1 << (32 - (int)$bits);
return (($ip_bin & $mask) === ($subnet_bin & $mask));
}
private function rateLimit() {
$key = 'metrics_limit:' . $_SERVER['REMOTE_ADDR'];
$current = apcu_fetch($key) ?: 0;
if ($current >= 60) { // 每分钟最多60次
http_response_code(429);
die('Too Many Requests');
}
apcu_store($key, $current + 1, 60);
}
}
// 使用安全验证
$security = new MetricsSecurity();
$security->validateAccess();
// 然后输出指标...
部署建议
# Docker Compose 示例
version: '3'
services:
php-app:
image: php:8.2-apache
volumes:
- ./:/var/www/html
ports:
- "8080:80"
environment:
- METRICS_ENABLED=true
- METRICS_API_KEY=your-secure-key
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
# prometheus.yml
scrape_configs:
- job_name: 'php-app'
scrape_interval: 15s
metrics_path: '/metrics'
basic_auth:
username: 'metrics'
password: 'your-secure-password'
static_configs:
- targets: ['php-app:80']
关键要点
- 性能优化:使用 Redis/APCu 存储指标数据,避免阻塞主业务
- 安全防护:添加 IP 白名单、API Key 验证、速率限制
- 容器化改造:支持 Docker 部署,便于监控
- 多维度指标:包含系统指标(CPU、内存)和业务指标(请求数、延迟)
- 标准化格式:使用 Prometheus 标准格式,便于与主流监控系统集成
- 错误处理:确保指标暴露接口不会影响主业务
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播