长轮询PHP返回2个结果而不是一个

Aka*_*kar 13 javascript php ajax long-polling

我正在尝试创建一个像Facebook一样的发布系统.所以我做了一些关于Facebook如何做的研究,Facebook使用长轮询,所以我搜索了如何实现它,我实现它.我终于完成了它,我打开了Firefox和Chrome来测试它.在2或3个帖子之后它起作用,但之后它将复制结果.如下所示:

结果重复

顺便说一句,这是第一篇文章.

这是我的网络选项卡,在此过程中: 它发出3个请求而不是2个

它发出3个请求而不是1个.

最后这是我的代码:

init.js包含我的所有JavaScript代码

function getNewPosts(timestamp) {
  var t;
  $.ajax({
    url: 'stream.php',
    data: 'timestamp=' + timestamp,
    dataType: 'JSON',
})
  .done(function(data) {
    clearInterval( t );
    // If there was results or no results
    // In both cases we start another AJAX request for long polling after 1 second
    if (data.message_content == 'results' || data.message_content == 'no-results') {
        t = setTimeout( function() {
            getNewPosts(data.timestamp);
        }, 1000);
        // If there was results we will append it to the post div
        if (data.message_content ==  'results') {
            // Loop through each post and output it to the screen
            $.each(data.posts, function(index, val) {
                $("<div class='post'>" + val.post_content + "<div class='post_datePosted'>"+ val.posted_date +"</div> <br>" + "</div>").prependTo('.posts');
            });
        }
    }
})
}

$(document).ready(function(){

    // Start the autosize function
    $('textarea').autosize();

    // Create an AJAX request to the server for the first time to get the posts
    $.ajax({
        async: false,
        url: 'stream.php?full_page_reload=1',
        type: 'GET',
        dataType: 'JSON',
    })
    .done(function(data) {
        // Assign the this variable to the server timestamp
        // that was given by the PHP script
        serverTimestamp = data.timestamp;
        $.each(data.posts, function(index, val) {
            $("<div class='post'>" + val.post_content + "<div class='post_datePosted'>"+ val.posted_date +"</div>" + "</div>").prependTo('.posts');
        });
    })
    .fail(function() {
        alert('There was an error!');
    })
    // When the form is submitted
    $('#post_form').on('submit', function(event) {
        $.ajax({
            url: 'ajax/post.php',
            type: 'POST',
            dataType: 'JSON',
            data: $('#post_form').serialize()
        })
        .done(function(data) {
            // Reset the form values
            $('#post_form')[0].reset();
        })
        .fail(function() {
            // When there was an error
            alert('An error occured');
        })
        // Prevent the default action
        event.preventDefault();
    });
    // Start the actual long polling when DOM is ready
    getNewPosts(serverTimestamp);
});
Run Code Online (Sandbox Code Playgroud)

还有我的stream.php

<?php
header('Content-type: application/json');
// If it was a full page reload
$lastId = isset($_GET['lastId']) && !empty($_GET['lastId']) ? $_GET['lastId'] : 0;
if (isset($_GET['full_page_reload']) && $_GET['full_page_reload'] == 1) {
    $first_ajax_call = (int)$_GET['full_page_reload'];

    // Create a database connection
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'akar', 'raparen');
    $sql = "SELECT * FROM `posts`";
    $stmt = $pdo->prepare($sql);
    $stmt->execute();
    $posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
    // Output the timestamp since its full page reload
    echo json_encode(array(
        'fullPageReload' => 'true',
        'timestamp' => time(),
        'posts' => $posts
        ));
} else if (isset($_GET['timestamp'])) {
    // The wasted time
    $time_wasted = 0;
    // Database connection
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'akar', 'raparen');
    $timestamp = $_GET['timestamp'];
    // Format the timestamp to SQL format
    $curr_time = date('Y-m-d H:i:s', $timestamp);
    $sql = "SELECT * FROM `posts` WHERE posted_date >= :curr_time";
    $stmt = $pdo->prepare($sql);
    $stmt->bindValue(':curr_time', $curr_time);
    $stmt->execute();
    // Fetch the results as an Associative array
    $posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
    // If there wasn't any results
    if ( $stmt->rowCount() <= 0 ) {
        // Create the main loop
        while ($stmt->rowCount() <= 0) {
            // If there is still no results or new posts
            if ($stmt->rowCount() <= 0) {
                // If we waited 60 seconds and still no results
                if ($time_wasted >= 60) {
                    die(json_encode(array(
                        'message_type' => 'error',
                        'message_content' => 'no-results',
                        'timestamp' => time()
                        )));
                }
                // Helps the server a little bit
                sleep(1);
                $sql = "SELECT * FROM `posts` WHERE posted_date >= :curr_time";
                $stmt = $pdo->prepare($sql);
                $stmt->bindParam(':curr_time', $curr_time);
                $stmt->execute();
                $posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
                // Increment the time_wasted variable by one
                $time_wasted += 1;
            }
        }
    }
    // If there was results then we output it.
    if ($stmt->rowCount() > 0) {
        die( json_encode( array(
            'message_content' => 'results',
            'timestamp' => time(),
            'posts' => $posts,
            )));
        exit();
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的ajax/post.php:

<?php
if ( isset($_POST['post_content']) ) {
    $post_content = strip_tags(trim($_POST['post_content']));
    if ( empty($post_content) ) {

        /* If the user doesn't enter anything */
        echo json_encode(array(
            'message_type' => 'error',
            'message_content' => 'It seems like your post is empty'
            ));
    } else {
        $pdo = new PDO('mysql:host=localhost;dbname=test', 'akar', 'raparen');
        $sql = "INSERT INTO `posts` (`post_id`, `post_content`, `posted_date`) VALUES (NULL, :post_content, NOW());";
        $stmt = $pdo->prepare($sql);
        $stmt->bindValue(':post_content', $post_content);
        $stmt->execute();
        echo json_encode(array(
            'message_type' => 'message',
            'message_content' => 'Your post has been posted successfully.'
            ));
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你不明白它就问我.我知道这是脏代码,我重复了很多.我这样做是为了测试,所以这并不重要.

谢谢!

kur*_*eko 1

坦率地说,我不明白你为什么要费心进行这种优化,除非你打算处理数千条消息。您也可以在每次刷新页面时获取全部内容。

无论如何,每秒向服务器发送来自每个客户端的请求都会产生大量流量,因此优化应该从定义更合理的轮询周期或更智能的自适应刷新机制开始,恕我直言。

现在,如果您确实想要这样做,则必须进行适当的同步。如果你弄乱了时间戳,你可以跳过其他人添加的消息,就像另一个客户端触发自动刷新一样,或者获取所述消息两次。

所有的超时处理都是不必要的。如果出现问题,通过 AJAX 的服务器请求将产生错误事件,这意味着连接或您的服务器出现故障,或者您的 PHP 端代码发生了一些问题并需要修复。

对于应用程序结构的想法:

  • 更新请求将向 PHP 传递一个时间戳,要求检索比该时间戳更新的所有帖子。初始值将为 1/1/1970 或其他值,以便初始提取检索所有现有帖子。新的时间戳将包含在响应中,以便增量请求将跳过已获取的数据。
  • JavaScript 将定期生成此类请求(我宁愿将时间段设置为 30 秒左右,以避免服务器负载过大 - 假设您的普通用户可以处理等待下一批伪推文那么长时间的挫败感)
  • 提交新帖子只需将其添加到数据库中,但由于所有操作都是在服务器端完成的,因此您无需担心竞争条件。

所有“time_wasted”和“cur_time”代码都应该扔进垃圾箱。唯一需要的时间参考是来自该特定客户端的最后一次读取请求的日期。
服务器端(在“流”PHP 文件中)所需的只是一个数据库请求,用于获取比客户端提供的时间戳更新的帖子,这将返回一个(可能为空)帖子列表以及同一时间戳的更新值。

坦率地说,您也可以使用最后获取的帖子的唯一标识符(对初始请求使用 0 或任何常规值),而不是这些可能令人困惑的时间戳。