PHP Curl Timeout 导致 Javascript 客户端崩溃

Posted

技术标签:

【中文标题】PHP Curl Timeout 导致 Javascript 客户端崩溃【英文标题】:PHP Curl Timeout crashing Javascript client side 【发布时间】:2017-01-06 22:37:02 【问题描述】:

我有一个自动同步,它向设备发送 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 请求是否引发了任何错误? @TheOneandOnlyChemistryBlob 不,如果我在 getDiagnosticStatus 中有回显“测试”,则网络打印 6-7 次。如果我在文件测试中只执行一个 curl 请求,则错误日志是: * 在 DNS 缓存中找到主机名 172.18.56.132 * 正在尝试 172.18.56.132 ... * 连接在 10000 毫秒后超时 * 正在关闭连接 0 如果问题在于网页停止,则发布 javascript,而不是 php 考虑不要在单个页面上发出 60 个请求,而是组合成一个更大的请求/响应。 已编辑后,现在使用 javascript。 @cale_b 问题是我使用了一个包含getDiagnosticStatus 方法的接口。所以每个面板都在实现该接口,因为每个面板都有不同的 IP 进行通信。我怎样才能结合所有?想象一下,第一个面板与成功沟通,但第二个没有。我将如何处理单个较大的请求? 【参考方案1】:

这不是复制/粘贴的完整解决方案,但它可以让您了解如何解决您的问题。您可以尝试下一个(仅限 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);

附言 在 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'] ...      
    

【讨论】:

感谢您的帮助!我正在分析你的答案。你的意思是使用 fastcgi_finish_request() ,所以客户端不要停止。然后用 getDataPartially 读取信息?我的疑问是 url,我应该把 getDataPartially 放在哪里?对不起,伙计,一个小新手:/ @BackSpace,对,您需要使用 getDataPartially 读取信息。为了实现您需要在 Controller 中添加操作并在 getDataPartially() 函数中调用此操作 url。我更新了我的答案。 目前我正在尝试实施您的答案。我用javascript代码更新了我的问题,你能看看吗?我认为问题出在php上,但我不知道。 您的 polling() 函数不是轮询的,它是常规的单个 ajax 请求。轮询工作在循环中,请参阅我的 getDataPartially() 函数。它每 2 秒运行一次,并通过 getDiagnosticStatus 更新数据。在 php 方面,您可以使用 $progressData['status'] = 'finish' 来回答,而在 js 方面,如果响应调用 clearInterval(timerId);它停止轮询请求。更多投票示例:***.com/questions/6835835/… 我在哪里保存了变量 memcache_obj?当调用 getDataPartially 操作时,由于未定义 memcache_obj,php 返回错误

以上是关于PHP Curl Timeout 导致 Javascript 客户端崩溃的主要内容,如果未能解决你的问题,请参考以下文章

PHP cURL:CURLOPT_CONNECTTIMEOUT 与 CURLOPT_TIMEOUT

PHP cURL 超时设置 CURLOPT_CONNECTTIMEOUT 和 CURLOPT_TIMEOUT 的区别

php curl 多线程方法

PHP curl 封装 GET及POST方法

PHP curl 封装 GET及POST方法很不错的

php获取post参数的几种方式