PHP Curl超时导致JavaScript客户端崩溃

3
我有一个自动同步功能,它会发送CURL请求到我的设备。我需要对每个设备都执行此操作(大约有60个)。问题是:
- 如果通信成功,一切都正常。
- 但如果通信失败,网页将等待超时时间结束。因此,在这3-4分钟内,客户端会崩溃...我有重要的网格,这些网格停止在那3-4分钟内加载数据。
自动同步是一个javascript函数,它执行AJAX请求调用下面的php控制器。我该如何防止这种情况发生呢?我不知道还有什么尝试的办法... AJAX是异步的,所以我不明白为什么网页会停止。
控制器:
$list = $panels_repository->getNetwork();

            $thread = new PollingThread($list);
            $thread->start();
            $thread->join();

            $result = $thread->result;
            $resultLength = sizeof($result);

            //...

线程:

class PollingThread extends Thread {
private $panels_list;
private $alarm_status;
public $result;

public function __construct($list) {
    $this->panels_list = $list;
}

public function run() {
    $panels_list = $this->panels_list;

    $alarmsUpdated = array();
    $panels = array();

    foreach($panels_list as $panel) {
        $alarms_list = $panel->getAlarmsList();

        //Get updated alarms status
        $panel->getDiagnosticStatus($alarms_list);

        //Save the results
        array_push($alarmsUpdated, $alarms_list);
    }

    $this->result = $alarmsUpdated;

}  

获取诊断状态

$input = "<?xml version='1.0' encoding='ISO-8859-1'?>
                <?getParameters message?>
                <displayMLRequest xmlns='http://www.peek.se/DisplayML/' version='1.12'
                                  dateTime='2008-01-10T15:09:51+02:00'>
                    <getParameters/>
                </displayMLRequest>";

    $ch = curl_init(); 
    curl_setopt($ch, CURLOPT_URL, $url); //Set IP to communicate

    //Set POST XML Input
    curl_setopt( $ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $input);

    //Return response as string & TimeOuts
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);

    //Execute
    $output = curl_exec($ch); 

    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    curl_close($ch);  

Javascript:

    $('#stations_tree').on('changed.jstree', function (e, data) {
    //....

    var dataObject = {
        type: "Selected",
        childrenID: childrenID_array,
        parentsID: parentsID_array
    };

    $.ajax({
        type: "POST",
        url: "controllers/PanelsController/",
        data: dataObject,
        cache: false,
        success: function ()
        {
            $("#dg_selected_stops").jsGrid("loadData");
            $("#dg_selected_pids").jsGrid("loadData");
        }
    });

}).jstree({
    plugins: ["checkbox", "state", "types"],
    "types": {
        "default": {
            "icon" : false
        }
    },
    core: {
        data: {
            url: "json/stations.json",
            dataType: "json",
            success: function () {
                //Save Panels Network as session variable
                var dataObject = {
                    type: "Network"
                };

                $.ajax({
                    type: "POST",
                    url: "controllers/PanelsController/",
                    data: dataObject,
                    cache: false,
                    success: function() {
                        //Get EquipmentStatus for each panel
                        var dataType = {
                            type: "Save"
                        };

                        $.ajax({
                            type: "POST",
                            url: "controllers/EquipmentStatusController/",
                            data: dataType,
                            cache: false,
                            success: function () {
                                //Status Unknown
                                changeTreeIcons();
                                loadAlarmsData();

                                polling();
                            }
                        });   
                    }
                });
            }
        }
    }
});

函数轮询(Polling)- 当JS停止运行,直到超时

function polling() {
var dataObject = {
    type: "Polling",
};

$.ajax({
    type: 'POST',
    url: "controllers/DatabaseController/",
    data: dataObject,
    success: function(response) {
        //loadAlarmsData();
        //changeTreeIcons();
    }
});

编辑: 我已经检查过,如果在轮询开始后尝试进行AJAX请求,网页只会在php脚本完成后执行该请求。因此,阻碍的JS是使用ajax请求加载数据的表格。我该如何解决这个问题?


这些错误的CURL请求是否会抛出任何错误? - The One and Only ChemistryBlob
@TheOneandOnlyChemistryBlob 不是的,如果在getDiagnosticStatus中使用echo "test",则Web会打印6-7次。 如果我只在文件测试中执行一个curl请求,则错误日志为:
  • 主机名172.18.56.132在DNS缓存中找到
  • 正在尝试172.18.56.132...
  • 在10000毫秒后连接超时
  • 关闭连接0
- BackSpace
2
如果问题出现在网页停止运行上,请发布JavaScript代码,而不是PHP代码。 - Robert Parham
请考虑不要在单个页面上发出60个请求,而是将它们合并为一个更大的请求/响应。 - random_user_name
帖子已编辑,现在加入了JavaScript。@cale_b问题在于我使用了一个包含getDiagnosticStatus方法的接口。因此,每个面板都实现了该接口,因为每个面板都有不同的IP地址进行通信。我该如何将它们合并?假设第一个面板成功通信,但第二个面板没有。我如何使用单个更大的请求来处理它们? - BackSpace
1个回答

2

这并不是完整的复制/粘贴解决方案,但它可以给您提供解决问题的思路。您可以尝试以下方法(仅适用于php-fpm)。

在前端使用js运行同步,并调用您的控制器,在客户端的控制器中为浏览器发送响应,并在CURL操作开始之前调用fastcgi_finish_request()。这个函数将所有响应数据刷新到客户端并完成请求,但PHP脚本会继续工作。

...

$list = $panels_repository->getNetwork();

$key = 'my_unique_operation_key'; // it key need you for get data on client side
$resp = [
   'status'=>'start',
   'operation_key' => $key
];
echo json_encode($resp);
fastcgi_finish_request(); // close connection and continue ...

$thread = new PollingThread($list, $key); // send $key also
...

在轮询线程中:

...
foreach($panels_list as $panel) {
    $alarms_list = $panel->getAlarmsList();

    //Get updated alarms status
    $panel->getDiagnosticStatus($alarms_list);

    //Save the results
    array_push($alarmsUpdated, $alarms_list);

    ...
    // save operation progress for example in memcache
    $progressData = some data about progress and $alarmsUpdated, etc...
    $memcache_obj->set('operation_'.$key, json_encode($progressData));
}

在控制器中添加一个动作,该动作返回数据部分:
function getDataPartially_action(){
  $key = $_GET['key']; // not forget validate
  ...
  $jsonData = $memcache_obj->get('operation_'.$key);  // get current state from memcache by key
  echo $jsonData;
  exit();
}

在前端方面:

// call controller and start operation
$.ajax({
  url: '/controller/uri/here',
  beforeSend: function() {
    // here you can place spinner or progress bar ...
  },
  success: function(json) {
    // in json you get progress key after fastcgi_finish_request()
    // and run visual progress
    getDataPartially(key);
  }
});

// load data partially
function getDataPartially(key){
  var timerId = setInterval(function() {
    $.ajax({
      url: '/controller/uri/here/getDataPartially_action?key=' + key,      
      success: function(json) {
        // in json you have data for grids and progress info
        // if json contains finish info stop progress
        clearInterval(timerId);
        // hide progress bar and etc ...
    }
});}, 2000);
}

p.s. 在CURL函数中,您可以使用CURLOPT_PROGRESSFUNCTION来获取有关进度的更多信息:

$ch = curl_init();
    curl_setopt ...
    curl_setopt($curl, CURLOPT_PROGRESSFUNCTION, 'curl_progress_callback');
    ...
// where curl_progress_callback is:

    function curl_progress_callback($dltotal, $dlnow, $ultotal, $ulnow){
        $curlInfo = curl_getinfo($ch); // a lot info about connection
        echo $curlInfo['connect_time'];
        echo $curlInfo['http_code'] ...      
    }

@BackSpace,对的,你需要使用getDataPartially来部分读取信息。为了实现这一点,你需要在控制器中添加一个动作,并在getDataPartially()函数中调用这个动作的URL。我更新了我的答案。 - Alex K
目前我正在尝试实现您的答案。我已经更新了我的问题,并附上了JavaScript代码,请您看一下好吗?我认为问题可能出在PHP方面,但我不确定。 - BackSpace
你的polling()函数并没有进行轮询,它只是一个普通的单一ajax请求。轮询是通过getDataPartially()函数实现的。该函数每隔2秒运行一次,并获取由getDiagnosticStatus更新的数据。在php端,你可以使用$progressData['status'] = 'finish'来回答,在js端,如果响应中存在'finish',就调用clearInterval(timerId)来停止轮询请求。更多关于轮询的示例:https://dev59.com/J2w15IYBdhLWcg3wFH3f - Alex K
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - BackSpace
我给你提供了代码的简短版本,你需要安装并初始化memcached。在这里可以看到,非常简单:http://php.net/manual/en/memcached.set.php - Alex K
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接