Swoole 毫秒定时器实现服务监控与报警方案

场景介绍

在高并发、高可用的服务架构中,核心服务(如 Redis、API 接口服务等)的稳定性直接影响业务连续性。一旦服务宕机,即使是短时间中断也可能造成重大损失。传统 Linux 定时任务 crontab 最小粒度为分钟级,无法满足实时监控需求。

Swoole 提供的毫秒级定时器可实现高频次服务状态检测,结合报警机制(短信、邮件等),能快速响应服务异常,为运维人员争取修复时间,是保障服务高可用的重要方案。

环境要求

1. 基础环境

已安装 PHP 运行环境(版本需与 Swoole 扩展兼容)。

2. 核心依赖

已安装 Swoole 扩展(推荐使用最新稳定版,确保定时器功能兼容性)。

3. 系统权限

运行脚本的用户需具备执行系统命令(如 netstat)、读写日志文件的权限。

核心实现思路

1. 监控目标

通过端口检测服务存活状态(如 Redis 默认端口 6379、自定义服务端口 8811)。

2. 检测频率

利用 Swoole 毫秒定时器(swoole_timer_tick),每 2 秒执行一次检测(可按需调整频率)。

3. 报警逻辑

服务异常时触发报警(短信、邮件),并停止该服务的监控定时器,避免重复报警。

4. 后台运行

通过 nohup 命令让脚本在后台持续运行,确保监控不中断。

代码实现

脚本文件:ServiceMonitoring.php

<?php
declare(strict_types = 1);

/**
 * Swoole 服务监控类
 * 基于端口检测服务存活状态,结合毫秒定时器实现实时监控与报警
 */
class ServiceMonitoring
{
    /**
     * 监控指定端口的服务是否正常运行
     * 原理:通过 netstat 命令查询端口监听状态,统计监听进程数
     *
     * @param int $port 待监控服务端口号
     * @return bool 服务正常返回 true,异常返回 false
     */
    public function monitoringPort(int $port): bool
    {
        // 系统命令:查询指定端口的监听进程数(2>/dev/null 忽略错误输出)
        $shellCommand = "netstat -anp 2>/dev/null | grep {$port} | grep LISTEN | wc -l";

        // 执行 shell 命令并获取结果
        $result = shell_exec($shellCommand);

        // 结果判断:返回 1 表示端口正常监听(服务运行中),否则服务异常
        if (trim($result) != 1) {
            $errorMsg = date('Y-m-d H:i:s') . "---- 端口号为 {$port} 的服务已挂掉 -----". PHP_EOL;
            echo $errorMsg;

            // TODO:添加报警逻辑(按需启用)
            // $this->sendEmailAlert($port); // 发送邮件给管理员
            // $this->sendSmsAlert($port);   // 发送短信给管理员

            return false;
        } else {
            $successMsg = date('Y-m-d H:i:s') . "---- 端口号为 {$port} 的服务正常运行 -----". PHP_EOL;
            echo $successMsg;
            return true;
        }
    }

    /**
     * (可选)发送邮件报警
     * @param int $port 异常服务端口
     */
    private function sendEmailAlert(int $port): void
    {
        $to = 'admin@example.com'; // 管理员邮箱
        $subject = "【服务异常报警】端口 {$port} 服务宕机";
        $message = "报警时间:" . date('Y-m-d H:i:s') . "\n异常服务:端口 {$port} 的服务已停止运行,请及时排查修复!";
        // 邮件发送逻辑(可使用 PHP mail 函数或第三方邮件 SDK)
        mail($to, $subject, $message);
    }

    /**
     * (可选)发送短信报警
     * @param int $port 异常服务端口
     */
    private function sendSmsAlert(int $port): void
    {
        $phone = '13800138000'; // 管理员手机号
        $content = "【服务异常报警】端口 {$port} 的服务于 " . date('Y-m-d H:i:s') . " 宕机,请及时处理!";
        // 短信发送逻辑(对接第三方短信 API,如阿里云短信、腾讯云短信等)
        // $this->callSmsApi($phone, $content);
    }
}

/**
 * 初始化毫秒定时器,开始监控指定端口
 * 定时器频率:2000 毫秒(2 秒/次),可根据需求调整(如 1000 毫秒 = 1 秒/次)
 * 逻辑:服务异常时清除定时器,避免重复报警;服务修复后需重启脚本恢复监控
 */

// 监控端口 8811(示例:自定义 API 服务)
swoole_timer_tick(2000, function ($timerId) {
    $monitor = new ServiceMonitoring();
    if (!$monitor->monitoringPort(8811)) {
        swoole_timer_clear($timerId); // 服务异常,停止该端口监控
        echo date('Y-m-d H:i:s') . "---- 端口 8811 监控已暂停,待服务修复后重启脚本 -----\n";
    }
});

// 监控端口 6379(示例:Redis 服务)
swoole_timer_tick(2000, function ($timerId) {
    $monitor = new ServiceMonitoring();
    if (!$monitor->monitoringPort(6379)) {
        swoole_timer_clear($timerId); // 服务异常,停止该端口监控
        echo date('Y-m-d H:i:s') . "---- 端口 6379 监控已暂停,待服务修复后重启脚本 -----\n";
    }
});

代码说明

类与方法:

  • ServiceMonitoring 类:封装服务监控核心逻辑,包含端口检测、报警触发方法。

  • monitoringPort(int $port):核心检测方法,通过 netstat 命令查询端口监听状态,返回服务存活结果。

  • 可选报警方法:sendEmailAlertsendSmsAlert,可根据实际业务对接邮件/短信服务。

定时器逻辑:

  • swoole_timer_tick(2000, $callback):创建周期性定时器,每 2000 毫秒执行一次回调函数。

  • swoole_timer_clear($timerId):服务异常时清除定时器,避免重复发送报警信息。

脚本运行与日志配置

1. 后台运行脚本

由于脚本需要持续监控,需通过 nohup 命令让其在后台运行(断开 SSH 连接后仍可正常工作),命令格式如下:

nohup /usr/local/php/bin/php /data/script/swoole/ServiceMonitoring.php > /logs/script/monitor.log 2>&1 &

2. 参数详解

部分 说明
nohup 忽略挂起信号,确保脚本后台持续运行
/usr/local/php/bin/php PHP 解释器的绝对路径(需根据实际安装路径调整,可通过 which php 命令查询)
/data/script/swoole/ServiceMonitoring.php 监控脚本的绝对路径(需替换为实际存放路径)
> /logs/script/monitor.log 重定向脚本输出日志到指定文件(需确保目录存在且有写入权限)
2>&1 将错误输出合并到日志文件(便于排查脚本运行异常)
& 让命令在后台运行

实现效果与验证

1. 正常运行效果

脚本运行后,日志文件(monitor.log)会持续输出服务状态信息:

2024-05-20 14:30:00---- 端口号为 8811 的服务正常运行 -----
2024-05-20 14:30:00---- 端口号为 6379 的服务正常运行 -----
2024-05-20 14:30:02---- 端口号为 8811 的服务正常运行 -----
2024-05-20 14:30:02---- 端口号为 6379 的服务正常运行 -----

2. 服务异常效果

当某端口服务宕机时,日志会输出报警信息,并暂停该端口监控:

2024-05-20 14:30:04---- 端口号为 6379 的服务已挂掉 -----
2024-05-20 14:30:04---- 端口 6379 监控已暂停,待服务修复后重启脚本 -----

3. 服务恢复流程

  1. 运维人员通过报警信息(邮件/短信)排查并修复异常服务。

  2. 修复完成后,重新执行 nohup 命令启动脚本,恢复该端口的监控。

扩展与优化

1. 监控扩展:除端口检测外,可新增监控项,如:

  • 内存使用率:通过 free -m 命令监控服务器内存占用。

  • 硬盘空间:通过 df -h 命令监控磁盘剩余空间。

  • 接口响应时间:通过 curl 命令请求核心接口,判断响应是否超时。

2. 报警优化:

  • 避免报警风暴:可添加“连续 N 次检测异常才触发报警”的逻辑(如连续 3 次检测到服务宕机再发送报警)。

  • 多渠道报警:同时集成邮件、短信、企业微信/钉钉机器人,确保报警信息及时触达。

3. 脚本稳定性:

  • 添加进程守护:结合 Swoole Process 或第三方工具(如 Supervisor),确保脚本意外退出后自动重启。

  • 日志轮转:配置日志轮转工具(如 logrotate),避免日志文件过大。

4. 端口配置:将待监控端口整理为配置数组,便于批量管理,示例:

$monitorPorts = [8811, 6379, 8080]; // 待监控端口列表
foreach ($monitorPorts as $port) {
    swoole_timer_tick(2000, function ($timerId) use ($port) {
        $monitor = new ServiceMonitoring();
        if (!$monitor->monitoringPort($port)) {
            swoole_timer_clear($timerId);
            echo date('Y-m-d H:i:s') . "---- 端口 {$port} 监控已暂停,待服务修复后重启脚本 -----\n";
        }
    });
}

注意事项

1. 权限问题

确保运行脚本的用户有权限执行 netstat 命令(部分系统可能需要 root 权限)。

2. 端口占用

避免监控端口被非目标服务占用,导致误报警。

3. Swoole 版本

低版本 Swoole 可能存在定时器bug,务必确保版本 ≥ 2.2。

4. 资源占用

监控频率过高(如 ≤ 500 毫秒/次)可能占用较多系统资源,需根据服务器配置合理调整。