本文目录导读:

我来帮你设计一个PHP项目指标收集并与Prometheus集成的方案。
基础架构
graph LR
A[PHP应用] --> B[Prometheus客户端]
B --> C[/metrics端点]
C --> D[Prometheus Server]
D --> E[Grafana]
安装与配置
安装Prometheus PHP客户端
composer require promphp/prometheus_client_php
基础配置示例
<?php
// src/Metrics/PrometheusMetrics.php
use Prometheus\CollectorRegistry;
use Prometheus\Storage\Redis;
use Prometheus\Storage\APC;
use Prometheus\RenderTextFormat;
class PrometheusMetrics
{
private CollectorRegistry $registry;
private static ?self $instance = null;
public function __construct()
{
// 选择存储后端
$adapter = new Redis(['host' => '127.0.0.1', 'port' => 6379]);
// 或使用APC(单机适用)
// $adapter = new APC();
$this->registry = new CollectorRegistry($adapter);
}
public static function getInstance(): self
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
// HTTP请求指标
public function trackRequest(string $method, string $endpoint, int $statusCode, float $duration): void
{
$counter = $this->registry->getOrRegisterCounter(
'app',
'http_requests_total',
'Total HTTP requests',
['method', 'endpoint', 'status_code']
);
$counter->incBy(1, [$method, $endpoint, (string)$statusCode]);
$histogram = $this->registry->getOrRegisterHistogram(
'app',
'http_request_duration_seconds',
'HTTP request duration',
['method', 'endpoint'],
[0.1, 0.5, 1, 2, 5, 10]
);
$histogram->observe($duration, [$method, $endpoint]);
}
// 数据库查询指标
public function trackQuery(string $queryType, string $table, float $duration): void
{
$histogram = $this->registry->getOrRegisterHistogram(
'app',
'db_query_duration_seconds',
'Database query duration',
['query_type', 'table'],
[0.01, 0.05, 0.1, 0.5, 1, 2]
);
$histogram->observe($duration, [$queryType, $table]);
}
// 业务指标
public function incBusinessMetric(string $metricName, array $labels = []): void
{
$counter = $this->registry->getOrRegisterCounter(
'app_business',
$metricName,
'Business metric: ' . $metricName,
array_keys($labels)
);
$counter->incBy(1, array_values($labels));
}
// 渲染metrics
public function render(): string
{
$renderer = new RenderTextFormat();
return $renderer->render($this->registry->getMetricFamilySamples());
}
}
中间件实现
Laravel中间件
<?php
// app/Http/Middleware/PrometheusMiddleware.php
namespace App\Http\Middleware;
use Closure;
use App\Metrics\PrometheusMetrics;
class PrometheusMiddleware
{
public function handle($request, Closure $next)
{
$startTime = microtime(true);
$response = $next($request);
$duration = microtime(true) - $startTime;
$metrics = PrometheusMetrics::getInstance();
$metrics->trackRequest(
$request->method(),
$request->path(),
$response->status(),
$duration
);
return $response;
}
}
Symfony中间件
<?php
// src/Middleware/PrometheusMiddleware.php
namespace App\Middleware;
use PrometheusMetrics;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class PrometheusMiddleware implements HttpKernelInterface
{
private HttpKernelInterface $app;
public function handle(Request $request, int $type = self::MAIN_REQUEST, bool $catch = true)
{
$start = microtime(true);
$response = $this->app->handle($request, $type, $catch);
$duration = microtime(true) - $start;
$metrics = PrometheusMetrics::getInstance();
$metrics->trackRequest($request->getMethod(), $request->getPathInfo(), $response->getStatusCode(), $duration);
return $response;
}
}
Metrics端点
<?php
// routes/api.php (Laravel)
// 或直接在入口文件
Route::get('/metrics', function () {
$metrics = PrometheusMetrics::getInstance();
// 添加系统指标
$metrics->registry->getOrRegisterGauge(
'system',
'memory_usage_bytes',
'Current memory usage'
)->set(memory_get_usage(true));
$metrics->registry->getOrRegisterGauge(
'system',
'memory_peak_usage_bytes',
'Peak memory usage'
)->set(memory_get_peak_usage(true));
return response($metrics->render(), 200, [
'Content-Type' => 'text/plain; charset=utf-8'
]);
});
Prometheus配置
# prometheus.yml
scrape_configs:
- job_name: 'php_app'
scrape_interval: 15s
static_configs:
- targets: ['localhost:8080']
metrics_path: '/metrics'
- job_name: 'php_app_advanced'
scrape_interval: 10s
metrics_path: '/metrics'
static_configs:
- targets:
- 'app1.example.com:8080'
- 'app2.example.com:8080'
relabel_configs:
- source_labels: [__address__]
regex: '(.*):.*'
target_label: instance
replacement: '${1}'
自定义业务指标
<?php
// Services/BusinessMetrics.php
class BusinessMetrics
{
private PrometheusMetrics $metrics;
public function __construct()
{
$this->metrics = PrometheusMetrics::getInstance();
}
// 用户注册指标
public function trackUserRegistration(string $source, string $country): void
{
$this->metrics->registry->getOrRegisterCounter(
'business',
'user_registrations_total',
'Total user registrations',
['source', 'country']
)->incBy(1, [$source, $country]);
}
// 订单指标
public function trackOrder(string $status, string $paymentMethod, float $amount): void
{
$this->metrics->registry->getOrRegisterCounter(
'business',
'orders_total',
'Total orders',
['status', 'payment_method']
)->incBy(1, [$status, $paymentMethod]);
$this->metrics->registry->getOrRegisterCounter(
'business',
'order_amount_total',
'Total order amount',
['status']
)->incBy($amount, [$status]);
}
// 并发用户数
public function trackConcurrentUsers(int $count): void
{
$this->metrics->registry->getOrRegisterGauge(
'business',
'concurrent_users',
'Number of concurrent users'
)->set($count);
}
// 队列指标
public function trackQueue(string $queueName, int $size, int $processed): void
{
$this->metrics->registry->getOrRegisterGauge(
'queue',
'queue_size',
'Queue size',
['queue_name']
)->set($size, [$queueName]);
$this->metrics->registry->getOrRegisterCounter(
'queue',
'messages_processed_total',
'Total messages processed',
['queue_name']
)->incBy($processed, [$queueName]);
}
}
Redis性能指标监控
<?php
// Monitoring/RedisMonitor.php
class RedisMonitor
{
private PrometheusMetrics $metrics;
private \Redis $redis;
public function __construct(\Redis $redis)
{
$this->redis = $redis;
$this->metrics = PrometheusMetrics::getInstance();
}
public function collectRedisMetrics(): void
{
$info = $this->redis->info();
// Redis关键指标
$gauges = [
'redis_connected_clients' => $info['connected_clients'],
'redis_used_memory_bytes' => $info['used_memory'],
'redis_used_memory_rss_bytes' => $info['used_memory_rss'],
'redis_hit_rate' => $this->calculateHitRate($info),
'redis_instantaneous_ops_per_sec' => $info['instantaneous_ops_per_sec'],
'redis_keyspace_hits' => $info['keyspace_hits'],
'redis_keyspace_misses' => $info['keyspace_misses']
];
foreach ($gauges as $name => $value) {
$this->metrics->registry->getOrRegisterGauge(
'redis',
$name,
'Redis metric: ' . $name
)->set($value);
}
}
private function calculateHitRate(array $info): float
{
$hits = $info['keyspace_hits'];
$misses = $info['keyspace_misses'];
$total = $hits + $misses;
return $total > 0 ? ($hits / $total) * 100 : 0;
}
}
Grafana仪表板配置
{
"dashboard": {: "PHP Application Metrics",
"panels": [
{
"title": "HTTP Requests Rate",
"type": "graph",
"targets": [
{
"expr": "rate(app_http_requests_total[5m])",
"legendFormat": "{{method}} {{endpoint}}"
}
]
},
{
"title": "Request Duration (95th Percentile)",
"type": "heatmap",
"targets": [
{
"expr": "histogram_quantile(0.95, sum(rate(app_http_request_duration_seconds_bucket[5m])) by (le))"
}
]
},
{
"title": "Database Query Duration",
"type": "graph",
"targets": [
{
"expr": "rate(app_db_query_duration_seconds_sum[5m]) / rate(app_db_query_duration_seconds_count[5m])",
"legendFormat": "{{query_type}} - {{table}}"
}
]
},
{
"title": "System Memory",
"type": "gauge",
"targets": [
{
"expr": "system_memory_usage_bytes"
}
]
}
]
}
}
性能优化建议
批量采集
<?php
// 使用APCu本地缓存,减少Redis写入
class BatchMetricsCollector
{
private array $buffer = [];
private int $bufferSize = 100;
public function record(string $metric, array $labels, float $value): void
{
$this->buffer[] = [
'metric' => $metric,
'labels' => $labels,
'value' => $value,
'timestamp' => microtime(true)
];
if (count($this->buffer) >= $this->bufferSize) {
$this->flush();
}
}
public function flush(): void
{
// 批量写入Redis或数据库
// ...
$this->buffer = [];
}
}
异步发送
<?php
// 使用消息队列异步处理
class AsyncMetricsSender
{
private Queue $queue;
public function send(array $metrics): void
{
// 将metrics推送到消息队列
$this->queue->push(json_encode($metrics));
}
}
Alerts规则
# alerts.yml
groups:
- name: php_app_alerts
rules:
- alert: HighErrorRate
expr: rate(app_http_requests_total{status_code=~"5.."}[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
description: "Error rate is {{ $value }} for the last 5 minutes"
- alert: SlowRequests
expr: histogram_quantile(0.95, rate(app_http_request_duration_seconds_bucket[5m])) > 2
for: 5m
labels:
severity: warning
annotations:
description: "95th percentile request duration is {{ $value }}s"
- alert: HighMemoryUsage
expr: system_memory_usage_bytes > 500000000
for: 10m
labels:
severity: warning
annotations:
description: "Memory usage is {{ $value }} bytes"
这套方案提供了完整的PHP与Prometheus集成方案,覆盖了基础监控、业务指标、性能优化和告警配置,你可以根据实际需求调整存储后端和具体指标。