我正在尝试创建一个像Facebook一样的发布系统。所以我做了一些关于Facebook如何做的研究,Facebook使用长轮询,所以我搜索了如何实现它,我实现它。我终于完成了它,我打开了Firefox和Chrome来测试它。在2或3个帖子之后它起作用,但之后它将复制结果。如下所示:
顺便说一句,这是第一篇文章。
这是我的网络标签,在此过程中:
它发出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);
});
我的 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();
}
}
这是我的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.'
));
}
}
如果你不明白它就问我。我知道这是脏代码,我重复了很多。我这样做是为了测试,所以它并不重要。
谢谢!
答案 0 :(得分:1)
我知道这并不能完全回答你的问题,但是你所做的事情无论如何都不会起作用 - 使用PHP进行长时间轮询会在至少有一些用户时崩溃你的服务器。您使用sleep
,因此PHP进程“挂起”。 PHP工作者计数(Apache,nginx或任何带PHP的服务器)都是有限的。一旦达到该计数,新连接将被拒绝。 PHP旨在快速给出响应。
对于这种类型的解决方案,我建议使用一些专为此设计的中间软件。例如,请查看Socket.IO。
它是用Javascript编写的,适用于客户端(JS库)和服务器端(Node.js)。您的Node.js服务器可以使用REST API,队列(如ZeroMQ,RabbitMQ等)或任何其他传输(如socket.IO client itself)从PHP中获取事件。这样您就不会在PHP中轮询数据库,只需将新帖子添加到Node.js服务器,该服务器将此信息传递给客户端JS代码。
$pdo->prepare('INSERT INTO ..')->execute();
$dispatcher->dispatch('new_post', new PostEvent(array('id' => 123, 'text' => 'Post text')));
长轮询只是受支持的Socket.IO协议之一,到目前为止并不是最有效的协议。
如果你想避开Node.js,你可以使用Ratchet尝试ReactPHP,在客户端使用WebSockets。这可以作为单个php进程(从命令行运行),因此不是apache-way。
答案 1 :(得分:1)
坦率地说,除非你打算处理成千上万条消息,否则我不明白你为什么要进行这种优化。每次刷新页面时,您都可以获取整个批次。
每秒钟从每个客户端请求对服务器进行锤击将产生大量流量,因此优化应从定义更合理的轮询周期或更智能,适应性更新机制开始,恕我直言。
现在,如果你真的想要它,你必须进行适当的同步。如果你搞砸了时间戳,你可以跳过其他人添加的消息,就像另一个客户端触发自动刷新一样,或者获取所述消息两次。
所有超时处理都是不必要的。如果出现问题,通过Ajax发出的服务器请求将产生错误事件,这意味着连接或服务器发生故障,或者您的PHP端代码发出异议并需要修复。
了解应用程序结构:
你所有的&#34; time_wasted&#34;和&#34; cur_time&#34;代码应该转到bin。 唯一需要的时间是该特定客户的最后一次读取请求的日期 所有你需要的服务器端(在你的&#34;流&#34; PHP文件中)是一个数据库请求来获取比客户端提供的时间戳更新的帖子,这将返回一个(可能是空的)帖子列表和更新的值相同的时间戳。
坦率地说,除了这些可能令人困惑的时间戳之外,您还可以使用上次提取的帖子的唯一标识符(使用0或初始请求的任何常规值)。
答案 2 :(得分:1)
你可以用这个:
$pdo->prepare('INSERT INTO ..')->execute();
$dispatcher->dispatch('new_post', new PostEvent(array('id' => 123, 'text' => 'Post text')));
答案 3 :(得分:0)
我认为这里有不必要的代码。你需要的只是。 定义2个部分。 1-是你的表格。 2-是您的留言查看器。
好的,所以第一次加载表单时,转到数据库反向信息(可能是JSON)并填充你的viwer。 要做到这一点,在ajax完成之内,将php JSON转换为数组,创建一个循环。 对于每个元素,使用追加jquery来添加每个帖子。 http://api.jquery.com/append/
点击(提交)为evenemnt做同样的事情。 您必须先清除htm内容才能填充新帖子。
答案 4 :(得分:0)
正如您在评论中指出的那样,存在大量冗余代码,这使得难以诊断问题。这是值得整理的,只是为了让其他阅读代码的人能够更好地诊断问题。
审查了代码之后,我就能看到这一点。
getNewPosts()
getNewPosts()
返回您可以通过向各种功能添加console.log()
命令来验证此顺序。确切的顺序可能会有所不同,具体取决于服务器响应的速度。但是,基本问题是在步骤2开始时未设置serverTimestamp
值。
分辨率很容易,serverTimestamp
变量需要正确设置。为此,请将getNewPosts()
函数调用移至.done()
处理程序以获取完整加载ajax请求。此时,服务器返回了可用于进一步轮询的初始时间戳值。
// 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');
});
// Start the long poll loop
getNewPosts(serverTimestamp);
})
答案 5 :(得分:0)
您可以设置超时:
setTimeout()
但你正在使用
clearInterval()
要清除它,请改用clearTimeout()
。
答案 6 :(得分:0)
如果您重现问题,请在您的网络标签中查看显示该条目两次的请求并检查持续时间,您会看到响应时间超过1秒,即&#39; 1000&#39;因此,您的javascript中超时,我认为没有必要使用该超时。
所有工作正常并且只显示一次条目的请求,他们应该在“1000”之前得到服务器响应。 (1秒)您可以通过将时间轴属性悬停在网络标签中进行检查:
或者点击特定请求并切换到“定时”按钮。标签:
因此,根据您的代码,下面是导致显示两次条目的方案:
答案 7 :(得分:0)
我现在无法真正测试这一点,但我看到的最大问题是
t
setTimeout
和clearInterval
我将编写缩写代码,主要是为了保持概念性。我可以做的最大的评论是不要使用间隔,因为AJAX调用可能需要比你的间隔更长的时间。你只需在每次完成ajax时设置一个新的超时。
// create a closure around everything
(function() {
var timer,
lastTimeStamp = <?php echo some_timestamp; ?>;
function getNewPosts() {
if(timer) clearTimeout(timer);
$.ajax({
data: lastTimeStamp
// other values here
})
.done(function() {
if( there_is_data ) {
lastTimeStamp = data.timestamp;
// render your data here
}
})
.always(function() {
timer = setTimeout(getNewPosts, 1000);
});
}
$(document).ready(function() {
// don't do your first ajax call, just let getNewPosts do it below
// define your form submit code here
getNewPosts();
});
}());
答案 8 :(得分:0)
使用Java脚本时间戳而不是从PHP返回。
因为您在执行PHP时设置时间戳,而不是在javascript setTimeOut完成时设置时间戳。
因此可能是获取帖子的时间和时间,PHP文件中的帖子是相同的,并且发回该时间戳将使您再次获取该记录,因为它适合在给定条件下。
在setTimeOut之后设置时间戳将为您提供一个与发布时间不同的新时间戳。
答案 9 :(得分:0)
我认为要解决此问题,您需要unbind
之前提交的表单。我在创建的脚本中看到了类似的问题。
// When the form is submitted
$('#post_form').unbind('submit').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();
});
答案 10 :(得分:0)
它可能正在执行两次。
也可以贴在其他地方 可能是另一个函数叫同一个。
答案 11 :(得分:0)
// 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');
});
}
我认为在将数据添加到(&#39; .post&#39;)之前,您必须清除以前的数据。
例如。 第一次:ajax结果是post1
第二次:ajax结果是post1 + post2
- &GT; .prependTo(&#39; .posts&#39;)的结果是post1 + post1 + post2