怎样在PHP项目中实现短信告警?

wen java案例 2

本文目录导读:

怎样在PHP项目中实现短信告警?

  1. 使用第三方短信服务商(最推荐)
  2. 通过HTTP API接口(通用方案)
  3. 集成消息队列实现异步告警
  4. 完整告警系统示例
  5. 最佳实践建议

在PHP项目中实现短信告警主要有三种方式,我按照推荐程度和适用场景为你详细介绍:

使用第三方短信服务商(最推荐)

主流服务商对比

服务商 特点 价格参考
阿里云短信 国内最成熟,覆盖广 约0.045元/条
腾讯云短信 与微信生态整合好 约0.05元/条
Twilio 国际业务首选 约$0.0079/条

阿里云短信实现示例

<?php
// 1. 安装依赖
// composer require alibabacloud/dysmsapi-20170525
use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
class SmsAlert
{
    private $accessKeyId;
    private $accessKeySecret;
    private $signName; // 短信签名
    public function __construct()
    {
        $this->accessKeyId = '你的AccessKeyId';
        $this->accessKeySecret = '你的AccessKeySecret';
        $this->signName = '你的签名';
    }
    /**
     * 发送短信告警
     */
    public function sendAlert(string $phone, string $message): bool
    {
        try {
            AlibabaCloud::accessKeyClient(
                $this->accessKeyId,
                $this->accessKeySecret
            )->regionId('cn-hangzhou')->asDefaultClient();
            $result = AlibabaCloud::rpc()
                ->product('Dysmsapi')
                ->version('2017-05-25')
                ->action('SendSms')
                ->method('POST')
                ->host('dysmsapi.aliyuncs.com')
                ->options([
                    'query' => [
                        'PhoneNumbers' => $phone,
                        'SignName' => $this->signName,
                        'TemplateCode' => 'SMS_XXXXXXXX', // 短信模板ID
                        'TemplateParam' => json_encode([
                            'content' => $message
                        ]),
                    ],
                ])
                ->request();
            $response = $result->toArray();
            if ($response['Code'] === 'OK') {
                return true;
            }
            throw new \Exception("短信发送失败: " . $response['Message']);
        } catch (ClientException $e) {
            throw new \Exception("客户端错误: " . $e->getErrorMessage());
        } catch (ServerException $e) {
            throw new \Exception("服务端错误: " . $e->getErrorMessage());
        }
    }
}
// 使用示例
$sms = new SmsAlert();
try {
    $sms->sendAlert('13800138000', '服务器CPU使用率超过90%,请及时处理');
} catch (\Exception $e) {
    // 记录日志
    error_log($e->getMessage());
}

通过HTTP API接口(通用方案)

<?php
class HttpSmsAlert
{
    private $apiUrl;
    private $apiKey;
    public function __construct()
    {
        // 以某第三方接口为例
        $this->apiUrl = 'https://api.smsprovider.com/send';
        $this->apiKey = 'YOUR_API_KEY';
    }
    public function sendSms(string $phone, string $content): bool
    {
        $data = [
            'phone' => $phone,
            'content' => $content,
            'apikey' => $this->apiKey,
        ];
        // 使用cURL发送请求
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->apiUrl);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($httpCode === 200) {
            $result = json_decode($response, true);
            return $result['status'] === 'success';
        }
        return false;
    }
}

集成消息队列实现异步告警

<?php
// 使用Redis作为消息队列
class AsyncSmsAlert
{
    private $redis;
    private $smsService;
    public function __construct()
    {
        $this->redis = new Redis();
        $this->redis->connect('127.0.0.1', 6379);
        $this->smsService = new SmsAlert(); // 使用上面的阿里云类
    }
    /**
     * 添加告警任务到队列
     */
    public function addAlertTask(string $phone, string $message): void
    {
        $task = [
            'phone' => $phone,
            'message' => $message,
            'time' => time(),
        ];
        $this->redis->lPush('sms_alert_queue', json_encode($task));
    }
    /**
     * 消费队列中的告警任务(在后台进程中运行)
     */
    public function consumeAlertQueue(): void
    {
        while (true) {
            $task = $this->redis->brPop('sms_alert_queue', 5);
            if ($task) {
                $data = json_decode($task[1], true);
                try {
                    $this->smsService->sendAlert(
                        $data['phone'], 
                        $data['message']
                    );
                    // 记录发送日志
                    $this->logAlert($data);
                } catch (\Exception $e) {
                    // 发送失败,重新入队或记录错误
                    $this->redis->lPush('sms_alert_failed', $task[1]);
                    error_log("短信发送失败: " . $e->getMessage());
                }
            }
        }
    }
    private function logAlert(array $data): void
    {
        $log = sprintf(
            "[%s] 发送告警到 %s: %s\n",
            date('Y-m-d H:i:s'),
            $data['phone'],
            $data['message']
        );
        file_put_contents('/var/log/sms_alert.log', $log, FILE_APPEND);
    }
}

完整告警系统示例

<?php
class AlertManager
{
    private $smsService;
    private $alertRules = [];
    private $recipients = [];
    public function __construct()
    {
        $this->smsService = new SmsAlert();
        $this->initConfig();
    }
    /**
     * 初始化配置
     */
    private function initConfig(): void
    {
        // 告警规则
        $this->alertRules = [
            'cpu_high' => [
                'threshold' => 90,
                'message' => 'CPU使用率已超过90%',
            ],
            'memory_low' => [
                'threshold' => 500, // MB
                'message' => '可用内存低于500MB',
            ],
            'disk_full' => [
                'threshold' => 90,
                'message' => '磁盘使用率超过90%',
            ],
        ];
        // 接收人
        $this->recipients = [
            '13800138000',
            '13900139000',
        ];
    }
    /**
     * 检查系统状态并发送告警
     */
    public function checkAndAlert(): void
    {
        $alerts = [];
        // 检查CPU
        $cpuUsage = $this->getCpuUsage();
        if ($cpuUsage > $this->alertRules['cpu_high']['threshold']) {
            $alerts[] = $this->alertRules['cpu_high']['message'] . 
                       " (当前: {$cpuUsage}%)";
        }
        // 检查内存
        $memoryFree = $this->getFreeMemory();
        if ($memoryFree < $this->alertRules['memory_low']['threshold']) {
            $alerts[] = $this->alertRules['memory_low']['message'] . 
                       " (当前: {$memoryFree}MB)";
        }
        // 检查磁盘
        $diskUsage = $this->getDiskUsage();
        if ($diskUsage > $this->alertRules['disk_full']['threshold']) {
            $alerts[] = $this->alertRules['disk_full']['message'] . 
                       " (当前: {$diskUsage}%)";
        }
        // 发送告警
        if (!empty($alerts)) {
            $message = implode("\n", $alerts);
            foreach ($this->recipients as $phone) {
                try {
                    $this->smsService->sendAlert($phone, $message);
                } catch (\Exception $e) {
                    error_log("发送到 {$phone} 失败: " . $e->getMessage());
                }
            }
        }
    }
    private function getCpuUsage(): float
    {
        $load = sys_getloadavg();
        return $load[0] * 100; // 转换为百分比
    }
    private function getFreeMemory(): float
    {
        if (PHP_OS_FAMILY === 'Linux') {
            $free = shell_exec("free -m | awk 'NR==2{print $4}'");
            return (float) trim($free);
        }
        return 1000; // Windows下返回默认值
    }
    private function getDiskUsage(): float
    {
        $total = disk_total_space("/");
        $free = disk_free_space("/");
        if ($total > 0) {
            return (($total - $free) / $total) * 100;
        }
        return 0;
    }
}
// 使用示例
$alertManager = new AlertManager();
// 可以放在cron中定时执行
// * * * * * php /path/to/alert.php
$alertManager->checkAndAlert();
// 或者在特定事件触发时调用
$alertManager->addAlertTask('13800138000', '用户XXX进行了敏感操作');

最佳实践建议

配置管理

// 使用.env文件管理敏感信息
// composer require vlucas/phpdotenv
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
define('SMS_ACCESS_KEY', $_ENV['SMS_ACCESS_KEY']);
define('SMS_SECRET_KEY', $_ENV['SMS_SECRET_KEY']);

限流和频率控制

class RateLimiter
{
    private $redis;
    public function __construct()
    {
        $this->redis = new Redis();
        $this->redis->connect('127.0.0.1', 6379);
    }
    public function checkRateLimit(string $phone): bool
    {
        $key = "sms_limit:{$phone}:" . date('H');
        $count = $this->redis->incr($key);
        if ($count === 1) {
            $this->redis->expire($key, 3600);
        }
        // 每小时最多5条
        return $count <= 5;
    }
}

日志和监控

// 记录所有发送记录
class SmsLogger
{
    public static function log(string $phone, string $message, bool $success): void
    {
        $logData = [
            'time' => date('Y-m-d H:i:s'),
            'phone' => $phone,
            'message' => $message,
            'success' => $success,
        ];
        file_put_contents(
            '/var/log/sms_history.log',
            json_encode($logData) . PHP_EOL,
            FILE_APPEND
        );
    }
}

多通道切换

// 主服务商失败时切换到备用
class SmsMultiChannel
{
    private $channels = [];
    private $currentChannel = 0;
    public function sendSms(string $phone, string $message): bool
    {
        for ($i = 0; $i < count($this->channels); $i++) {
            try {
                $result = $this->channels[$i]->send($phone, $message);
                if ($result) {
                    return true;
                }
            } catch (\Exception $e) {
                continue;
            }
        }
        return false;
    }
}
  1. 小项目:直接使用阿里云/腾讯云短信SDK
  2. 中型项目:加入消息队列实现异步处理
  3. 大型项目:构建完整的告警系统,包括限流、日志、多通道

建议从阿里云短信开始实现,它提供完善的API和文档,成本也比较低,记得在生产环境中使用异步方式发送,避免阻塞主流程。

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