【问题标题】:Sending data from server to client?将数据从服务器发送到客户端?
【发布时间】:2013-04-11 21:45:32
【问题描述】:

我有一个 php 服务器文件和一个 HTML 客户端文件,该 HTML 文件向服务器发送 ajax 请求以检索每个 500 ms 的数据,尽管这按预期工作,但会导致客户端设备上的内存和 CPU 使用率很高。

PHP

if(isset($_POST['id']) && $_POST['id'] != '' )
{
    $id     = $_POST['id'];
    $select = $con->prepare("SELECT * FROM data WHERE id=?");
    $select->bind_param('s', $id);
    $select->execute();
    $result = $select->get_result();
    while($row = $result->fetch_assoc())
    {
        echo $row['column 1'] . "\t" . $row['column 2'] . "\n";
    }
}

AJAX

function send(){
    var formdata = new FormData(),
        id       = document.getElementById('id').value;
    formdata.append('id', id);
    var xhr = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
    xhr.open('post', 'server.php', true);
    xhr.send(formdata);
    xhr.onreadystatechange = function(){
        if(xhr.readyState == 4 && xhr.status == 200){
            console.log(xhr.responseText);
        }
    }
}
setInterval(function(){send()}, 500); 

我想找到一个 ajax 的替代解决方案,而不是大多数时间向服务器发送大量请求并检索相同的数据,如果服务器可以与客户端交互数据更改或更新,效率会更高.

我不能使用 PHP SocketHttpRequest 方法,因为它们没有安装在我的托管服务器上,我不确定后者是否有效。我能想到的唯一方法是使用SESSIONS

根据this PHP 服务器将所有用户会话存储在服务器上的同一目录中,因此可以直接在文件中更改特定用户的会话变量。然而问题是这些文件中的数据是序列化的,我不确定如何反序列化数据并重新序列化它们,然后保存新数据!

即使我能够找到在会话文件上存储更新的方法,我仍然需要使用 setInterval 来监听会话的变量更改每个500ms,虽然它并不理想,但它会比使用 @ 好得多987654329@ 在内存和 CPU 使用方面。

那么最好的方法是什么?任何帮助将不胜感激。


更新:

我意识到SESSION 不会工作,因为它只能由服务器而不是客户端读取,因此我必须向服务器发送 ajax 请求以获取我试图避免的变量。

我尝试过长轮询,但遇到了很多问题,flushob_flush() 在我的服务器上不起作用,我无法更改 ini 设置。在尝试无限循环时,我无法让它在数据更改时中断:

if(isset($_GET['size']) && $_GET['size'] != '')
{
    $size = (int)$_GET['size'];
    $txt = "logs/logs.txt";
    $newsize = (int)filesize($txt);    
    while(true) {
        if($newsize !== $size) {
            $data = array( "size" => filesize($txt), "content" => file_get_contents($txt));
            echo json_encode($data);
            break;
        }
        else{
            $newsize = (int)filesize($txt);
            usleep(400000);
        }
    }
    
}

它会一直持续下去,即使logs.txt 的大小增加它也不会中断!我怎样才能让它在大小增加时破坏和回显数据?

更新 2:

原来调用filesize()方法时php缓存了文件大小,因此上述循环将无限期运行,解决方案是使用clearstatcache()方法,它将清除文件大小的存储缓存,允许循环中断文件大小更改。

【问题讨论】:

  • 您希望您的响应数据多久更改一次?
  • 它会变化,可能是每 10 秒一次或每 10 分钟一次
  • 结果集有多大?以 kb 为单位?
  • 客户应该多快收到数据更改?多少秒是可以接受的?
  • 是的,我问的是数据更新的频率,因为每半秒轮询一次似乎相当极端。

标签: php ajax performance session


【解决方案1】:

好的,经过多次测试和长期研究,我得出的结论是 PHP 服务器永远不会直接与指定的客户端交互,除非客户端先向服务器发送请求。

我发现唯一可靠的解决方案是使用只会在数据更改时中断的无限循环,这将大大降低对服务器的 ajax 请求频率,从而提高性能并减少内存和 CPU 的使用率客户的设备,这里是怎么回事:

PHP 1(处理数据更新或向数据库插入新数据):

$process = $_POST['process'];
$log = "/logs/logs.txt";

if($process == 'update'){
    //execute mysqli update command and update table.
    $str = "Update on " . date('d/m/Y - H:i:s') . "\n";//add some text to the logs file (can be anything just to increase the logs.text size)
    file_put_content($log, $str, FILE_APPEND);//FILE_APPEND add string to the end of the file instead or replacing it's content
}
else if($process == 'insert'){
    //execute mysqli insert command and add new data to table.
    $str = "Added new data on" . date('d/m/Y - H:i:s') . "\n";
    file_put_content($log, $str, FILE_APPEND);
}

上面的代码将插入/更新数据,创建文件log.txt(如果不存在),并在每个请求中添加额外的文本。 log.txt 稍后将在“下面”的无限循环中使用,并且会在循环大小发生变化时中断循环。

PHP 2(处理读取数据请求):

if(isset($_POST['id']) && $_POST['id'] != '' && isset($_POST['size']) && $_POST['size'] != '')
{
    $id         = (string)$_POST['id'];
    $init_size  = (int)$_POST['count'];
    $size       = file_exists('logs/logs.txt') ? (int)filesize('logs/logs.txt') : 0;//$size is logs.txt size or 0 if logs.txt doesn't exist(not created yet).

    $select = $con->prepare("SELECT * FROM data WHERE id=?");
    $select->bind_param('s', $id);

    while(true){ //while(true) will loop indefinitely because condition true is always met
        if($init_size !== $size){
            $select->execute();
            $result = $select->get_result();
            while($row = $result->fetch_assoc())
            {
                $data['rows'][] = array(
                                  "column 1" => $row['column 1'],
                                  "column 2" => $row['column 2'],
                                  );

            }
            $data['size'] = $size;
            echo json_encode($data);
            break; //break the loop when condition ($init_size != $size) is met which indicates that database has been updated or new data has been added to it.
        }
        else{
            clearstatcache(); //clears the chached filesize of log.txt
            $size = file_exists('logs/logs.txt') ? (int)filesize('logs/logs.txt') : 0;
            usleep(100000) //sleep for 100 ms
        }
    }
}

AJAX:

var size = 0; //declares global variable size and set it's initial value to 0

function send(s){
    var formdata = new FormData(),
        id       = document.getElementById('id').value;
    formdata.append('id', id);
    formdata.append('size', s);
    var xhr = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
    xhr.open('post', 'server.php', true);
    xhr.timeout = 25000; //set timeout on xmlhttprequest to 25 sec, some servers has short execution tiemout, in my case it's 27 sec so i set the value to 25 sec.
    xhr.send(formdata);
    xhr.onreadystatechange = function(){
        if(xhr.readyState == 4 && xhr.status == 200){
            var data = JSON.parse(xhr.responseText);
            size = data.size;
            console.log(data.rows);
            setTimeout(function(){send(size)}, 100); //re-initiate the request after receiving data 
        }
    }
    xhr.ontimeout = function(){
        xhr.abort(); //abort the timed out xmlhttp request
        setTimeout(function(){send(size)}, 100);
}
send(size); 

这不是理想的解决方案,但它将我的 xmlhttp 请求从 2/sec 减少到低至 1/25 sec,希望有人能够提出更好的解决方案。

【讨论】:

【解决方案2】:

在我们能够在浏览器中使用套接字之前,我们使用了长轮询。基本思想是浏览器不会定期发出请求,而是浏览器会向服务器发出请求,但服务器不会响应,直到有值得分享的内容返回给浏览器。这意味着请求可能会保持打开状态 10 毫秒或数小时。

在服务器响应某些内容后,浏览器的工作就是发出新的 ajax 请求。这样,始终有一条线路向服务器开放。

请参阅this question 了解更多信息。

【讨论】:

    【解决方案3】:

    回答您关于直接编辑会话的部分问题...

    要直接操作用户的会话,我假设您知道并可以跟踪任何用户的会话 ID(可能在登录时在您的数据库中)。

    当您需要直接在服务器上编辑用户会话时:

    1. 从数据库中检索用户的最后一个已知会话。
    2. 调用session_close() 关闭当前会话(如果有的话)。
    3. 使用会话 ID 调用 `session_name($sessionId)'。
    4. 致电session_open() 以打开该会话。 $_SESSION 应该填充会话数据。您无需反序列化任何内容。
    5. 对会话进行编辑。
    6. 调用session_close()重新序列化数据。

    或者,您可以直接打开会话文件,unserialize() 它,编辑数据,然后手动重新serialize()

    【讨论】:

    • 谢谢,但我意识到session 不起作用,因为它只能由服务器而不是客户端读取,所以我必须将xmlhttprequest 发送到服务器并返回到第一个。
    【解决方案4】:

    您可以向 php 脚本创建一个 ajax 请求,该脚本仅在有任何新数据时才返回数据。只要没有新数据,脚本就会一直循环运行,直到有。

    【讨论】:

      【解决方案5】:

      我认为今天这个问题的答案是使用 Websocket 阅读它。无需请求服务器,您可以获取数据,如果有东西将其更改为服务器

      link

      【讨论】:

        【解决方案6】:

        您可以使用SSEWebsocket 来实现此目的。

        【讨论】:

          猜你喜欢
          • 2021-12-07
          • 1970-01-01
          • 2012-08-29
          • 2020-08-17
          • 2021-01-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-01-16
          相关资源
          最近更新 更多