You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在接收MQTT消息时响应Ajax请求(PHP环境)

解决方案:分离MQTT监听与HTTP请求处理

因为MQTT客户端的loop(true)是阻塞式循环,会持续占用进程无法处理其他逻辑,所以不能把它和HTTP请求处理放在同一个脚本里。正确的做法是把MQTT监听做成独立的后台常驻进程,将收到的消息存入共享存储(比如Redis、数据库),而处理Ajax请求的PHP脚本只负责从存储中读取最新数据。

步骤1:编写独立的MQTT监听后台脚本

修改你的MQTT代码,将收到的消息存入Redis(性能最优,适合实时场景):

<?php 
require('vendor/autoload.php');
use PhpMqtt\Client\MqttClient;
use PhpMqtt\Client\ConnectionSettings;

// 初始化Redis连接(确保已安装Redis扩展)
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 如果Redis有密码,添加$redis->auth('your_password');

$server   = '<address>';
$port     = 1883;
$clientId = 'mqtt_listener_' . uniqid(); // 避免客户端ID重复
$username = '<user>';
$password = '<password>';

$mqtt = new MqttClient($server, $port, $clientId);

$connectionSettings = (new ConnectionSettings)
  ->setUsername($username)
  ->setPassword($password)
  ->setKeepAliveInterval(60)
  ->setLastWillQualityOfService(1);

$mqtt->connect($connectionSettings, true);
$mqtt->subscribe('wsc/power', function ($topic, $message) use ($redis) {
    // 将消息存入Redis,用topic作为键覆盖旧值(保留最新数据)
    $redis->set('mqtt:' . $topic, $message);
    // 如果需要保存历史消息,可用列表存储并限制长度
    // $redis->lpush('mqtt:' . $topic . ':history', $message);
    // $redis->ltrim('mqtt:' . $topic . ':history', 0, 99); // 只保留最新100条
}, 0);

// 启动阻塞循环监听MQTT消息
$mqtt->loop(true);

将这个脚本保存为mqtt_listener.php,然后后台启动:

# 用nohup让脚本后台运行,输出重定向到空设备
nohup php mqtt_listener.php > /dev/null 2>&1 &

更可靠的方式是用systemd制作服务,确保进程意外退出后自动重启。

步骤2:修改Ajax请求处理脚本,读取共享存储中的MQTT消息

在原有的HTTP处理脚本中,添加读取Redis数据的逻辑,响应前端的数据请求:

<?php
$result = [];
$result['success'] = false;
$result['message'] = "unknown";

// 初始化Redis连接
$redis = new Redis();
$redisConnectSuccess = $redis->connect('127.0.0.1', 6379);
// 处理Redis连接失败的情况
if (!$redisConnectSuccess) {
    $result['message'] = "数据存储服务连接失败";
    echo json_encode($result);
    exit;
}

if (isset($_POST['action'])) {
    switch ($_POST['action']) {
        case 'quit':
            $msg = shell_exec('sudo systemctl stop startup.service');
            $result['success'] = true;
            $result['message'] = $msg;
            break;

        case 'restart':
            $msg = shell_exec('sudo systemctl restart startup.service');
            $result['success'] = true;
            $result['message'] = $msg;
            break;

        case "get-power-data":
            // 从Redis读取最新的MQTT消息
            $powerData = $redis->get('mqtt:wsc/power');
            if ($powerData !== false) {
                $result['success'] = true;
                $result['data'] = $powerData;
                $result['message'] = "获取数据成功";
            } else {
                $result['message'] = "暂无数据";
            }
            break;
            
        case "do-something":
            // 执行自定义逻辑
            $result['success'] = true; // 根据实际逻辑修改为true/false
            $result['message'] = "操作完成";
            break;    
    }

    echo json_encode($result);
}

步骤3:前端通过Ajax轮询获取最新数据

前端定时发送Ajax请求调用get-power-data动作,即可获取MQTT推送的最新数据:

// 每3秒轮询一次
setInterval(() => {
    fetch('/your-php-script.php', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: 'action=get-power-data'
    })
    .then(response => response.json())
    .then(data => {
        if (data.success) {
            // 更新网页内容
            document.getElementById('power-data').textContent = data.data;
        }
    })
    .catch(error => console.error('请求失败:', error));
}, 3000);

关键注意事项

  • 确保Redis服务正常运行,PHP已安装Redis扩展(可通过php -m | grep redis检查)。
  • MQTT监听进程需要长期运行,建议用systemd或supervisor管理,避免意外退出。
  • 如果使用数据库替代Redis,注意处理并发写入的问题,确保数据一致性。

内容的提问来源于stack exchange,提问作者Mark

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 13:15:01