本文目录导读:

针对PHP项目内存使用率超标预警,可以从应用层监控、系统层监控、日志告警三个维度来设计预警方案,以下是具体的实现思路和代码示例:
应用层实时监控
在PHP代码中嵌入内存检查逻辑,当内存使用超过阈值时主动告警。
1 核心监控函数
<?php
/**
* 内存监控类
*/
class MemoryMonitor {
private $threshold; // 阈值(单位:MB)
private $alertCallback; // 告警回调函数
public function __construct($thresholdMB = 128, callable $alertCallback = null) {
$this->threshold = $thresholdMB * 1024 * 1024; // 转换为字节
$this->alertCallback = $alertCallback;
}
/**
* 检查当前内存使用
* @return bool 是否超标
*/
public function checkMemoryUsage() {
$usage = memory_get_usage(true); // 获取实际分配的内存
if ($usage > $this->threshold) {
$this->triggerAlert($usage);
return true;
}
return false;
}
/**
* 触发告警
*/
private function triggerAlert($usage) {
$message = sprintf(
"[MEMORY ALERT] 内存使用超标: %.2f MB (阈值: %.2f MB)\n",
$usage / 1024 / 1024,
$this->threshold / 1024 / 1024
);
// 1. 记录日志
error_log($message);
// 2. 调用自定义告警回调
if ($this->alertCallback) {
call_user_func($this->alertCallback, $usage, $this->threshold);
}
// 3. 发送通知(示例:邮件/钉钉/企业微信)
$this->sendNotification($message);
}
/**
* 发送告警通知(以钉钉Webhook为例)
*/
private function sendNotification($message) {
$webhookUrl = 'https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN';
$data = [
'msgtype' => 'text',
'text' => [
'content' => $message
]
];
$ch = curl_init($webhookUrl);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
}
}
// 使用示例
$monitor = new MemoryMonitor(256, function($usage, $threshold) {
// 自定义告警逻辑:例如写入Redis、更新数据库
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->lPush('memory_alerts', json_encode([
'time' => date('Y-m-d H:i:s'),
'usage' => $usage,
'threshold' => $threshold
]));
});
// 在关键代码段执行内存检查
$monitor->checkMemoryUsage();
2 自动监控中间件(适用于框架)
以Laravel为例,创建中间件自动监控每个请求的内存使用:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class MemoryMonitorMiddleware
{
public function handle(Request $request, Closure $next)
{
$response = $next($request);
// 请求结束后检查内存
$peakMemory = memory_get_peak_usage(true);
$threshold = 128 * 1024 * 1024; // 128MB
if ($peakMemory > $threshold) {
\Log::warning('请求内存超标', [
'url' => $request->fullUrl(),
'memory' => $peakMemory / 1024 / 1024 . 'MB',
'method' => $request->method()
]);
// 发送告警
// ...
}
return $response;
}
}
系统层监控
1 使用shell脚本监控所有PHP进程
#!/bin/bash
# php_memory_monitor.sh
# 配置
THRESHOLD_MB=256
LOG_FILE="/var/log/php_memory_alerts.log"
WEBHOOK_URL="https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN"
# 获取所有PHP进程的内存使用
php_pids=$(pgrep -f "php-fpm|php-cgi|php-cli")
for pid in $php_pids; do
if [ -d "/proc/$pid" ]; then
# 获取内存使用(单位:KB)
memory_kb=$(awk '/VmRSS/{print $2}' /proc/$pid/status 2>/dev/null)
if [ ! -z "$memory_kb" ]; then
memory_mb=$((memory_kb / 1024))
if [ $memory_mb -gt $THRESHOLD_MB ]; then
# 获取进程详细信息
cmdline=$(cat /proc/$pid/cmdline 2>/dev/null | tr '\0' ' ')
message="[MEMORY ALERT] PID: $pid, 内存: ${memory_mb}MB, 命令: $cmdline"
# 记录日志
echo "$(date '+%Y-%m-%d %H:%M:%S') $message" >> $LOG_FILE
# 发送告警
curl -s -X POST -H "Content-Type: application/json" \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$message\"}}" \
$WEBHOOK_URL &
fi
fi
fi
done
设置cron定时任务(每分钟检查):
* * * * * /usr/local/bin/php_memory_monitor.sh
2 使用Prometheus + Grafana监控
创建自定义exporter暴露PHP内存数据:
<?php
// metrics.php - 供Prometheus抓取的指标
header('Content-Type: text/plain; charset=utf-8');
$metrics = [];
// 当前内存使用
$currentMemory = memory_get_usage(true);
$peakMemory = memory_get_peak_usage(true);
$metrics[] = "# HELP php_memory_bytes Current PHP memory usage in bytes";
$metrics[] = "# TYPE php_memory_bytes gauge";
$metrics[] = "php_memory_bytes{type=\"current\"} $currentMemory";
$metrics[] = "php_memory_bytes{type=\"peak\"} $peakMemory";
// 输出指标
echo implode("\n", $metrics) . "\n";
Prometheus告警规则:
groups:
- name: php_memory_alerts
rules:
- alert: PHPHighMemoryUsage
expr: php_memory_bytes{type="current"} > 256000000
for: 1m
labels:
severity: warning
annotations:
summary: "PHP内存使用超标"
description: "当前内存使用 {{ $value | humanize }},超过阈值 256MB"
日志告警方案
1 使用ELK+Watcher进行日志告警
- 在PHP中记录内存告警日志:
<?php // 日志格式:JSON格式便于解析 $logData = [ 'type' => 'memory_alert', 'timestamp' => date('Y-m-d\TH:i:s\Z'), 'memory_usage' => memory_get_usage(true), 'peak_memory' => memory_get_peak_usage(true), 'script' => $_SERVER['SCRIPT_NAME'] ?? 'unknown', 'pid' => getmypid() ];
error_log(json_encode($logData) . "\n", 3, '/var/log/php_memory.json.log');
2. 配置Logstash解析日志,发送到Elasticsearch
3. 在Kibana中创建Watcher告警:
```json
{
"trigger": {
"schedule": {
"interval": "1m"
}
},
"input": {
"search": {
"request": {
"indices": ["php-logs-*"],
"body": {
"query": {
"bool": {
"filter": [
{"term": {"type": "memory_alert"}},
{"range": {"memory_usage": {"gte": 268435456}}}
]
}
}
}
}
}
},
"actions": {
"email_admin": {
"email": {
"to": "admin@example.com",
"subject": "PHP内存超标告警",
"body": "发现 {{ctx.payload.hits.total}} 条内存超标记录"
}
}
}
}
完善预警体系建议
1 多级阈值设置
| 级别 | 阈值 | 处理方式 |
|---|---|---|
| WARNING | 80%阈值 | 仅记录日志 |
| CRITICAL | 100%阈值 | 发送告警通知 |
| EMERGENCY | 150%阈值 | 自动重启进程 |
2 告警渠道整合
<?php
class AlertManager {
private $channels = [];
public function addChannel($name, callable $handler) {
$this->channels[$name] = $handler;
}
public function sendAlert($level, $message, $context = []) {
foreach ($this->channels as $name => $handler) {
try {
$handler($level, $message, $context);
} catch (\Exception $e) {
error_log("告警渠道 {$name} 发送失败: " . $e->getMessage());
}
}
}
}
// 使用示例
$alertManager = new AlertManager();
$alertManager->addChannel('email', function($level, $msg, $ctx) {
// 发送邮件
});
$alertManager->addChannel('wechat', function($level, $msg, $ctx) {
// 发送企业微信
});
$alertManager->addChannel('sms', function($level, $msg, $ctx) {
// 发送短信(仅紧急级别)
if ($level === 'EMERGENCY') {
// 发送短信
}
});
$alertManager->sendAlert('CRITICAL', '内存使用超标', [
'memory' => '300MB',
'threshold' => '256MB',
'pid' => 12345,
'script' => '/path/to/script.php'
]);
3 告警抑制机制
避免重复告警骚扰:
<?php
class AlertSuppressor {
private $redis;
private $cooldown = 300; // 5分钟冷却时间
public function shouldSend($alertKey) {
$key = "alert_suppress:{$alertKey}";
if ($this->redis->exists($key)) {
return false; // 冷却期内不发送
}
// 设置冷却期
$this->redis->setex($key, $this->cooldown, 1);
return true;
}
}
最佳实践建议
-
选择适合的监控粒度:
- 线上环境:每分钟检查一次
- 重要业务接口:每次请求都检查
- 批量处理脚本:每处理N条数据检查一次
-
告警分级:
- P0级别:内存使用>90%阈值,立即电话通知
- P1级别:>80%阈值,发送即时消息
- P2级别:>70%阈值,记录日志
-
结合性能分析:
- 内存超标时自动生成内存快照(memory_get_usage配合debug_backtrace)
- 使用Xdebug profiling分析内存泄漏
-
自动化处理:
- 自动重启高内存进程
- 自动扩展资源(如增加FPM进程数)
通过以上方案组合,可以实现对PHP项目内存使用的全方位监控预警,确保问题能够及时发现和处理。