PHP的长轮询“杀死”服务器

时间:2018-11-04 10:57:04

标签: php mysql

我正在开发网站上用户之间的在线聊天。聊天功能正常,但是如果长时间的轮询连接打开,则没有人可以进入网站。

我的代码[更新]:

$time = $_GET['time'];
while (true) {
    $sth = $db->prepare("SELECT * FROM messages LEFT JOIN users ON users.username=messages.chat_msg_user_id WHERE chat_msg_client_id=:chat_msg_client_id AND chat_msg_id>:chat_msg_id AND chat_notification='0' ORDER BY chat_msg_time DESC LIMIT 1");
    $sth->execute([":chat_msg_client_id" => $client_id, ":chat_msg_id" => $last_message_id]);
    $messages = [];
    while ($answer = $sth->fetch()) {
        $msg = ["chat_msg_id" => $answer["chat_msg_id"], "chat_msg_user_id" => $answer["chat_msg_user_id"], "chat_username" => $answer['username'], "chat_user_photo" => $answer['mainphoto'], "chat_user_status" => $answer['status'], "chat_user_name" => $answer["name"], "chat_msg_from" => $answer['chat_msg_from'], "chat_msg_time" => date("H:i", $answer["chat_msg_time"]), "chat_msg_date" => date("m.d.y", $answer["chat_msg_time"]), "chat_msg_text" => mb_str_replace("\n", "<br>", $answer["chat_msg_text"]), "read" => $answer['chat_read'], ];
        $messages[] = $msg;
        $last_message_id = $answer["chat_msg_id"];

        // some variables here for json_encode below //

    }
    if (count($messages) > 0) {
        $sth2 = $db->prepare("SELECT count(chat_read) as unread_messages_count FROM messages WHERE chat_msg_client_id='$client_id' AND chat_read='0'");
        $sth2->execute();
        $answers = $sth2->fetch();
        $unread_messages_count = $answers['unread_messages_count'];
        echo json_encode(["command" => "new_messages", "messages" => $messages, "last_message_id" => $last_message_id, "chat_msg_id" => $chat_msg_id, "chat_user_name" => $chat_user_name, "chat_user_status" => $chat_user_status, "chat_user_photo" => $chat_user_photo, "chat_msg_from" => $chat_msg_from, "chat_msg_time" => $chat_msg_time, "chat_msg_date" => $chat_msg_date, "chat_msg_text" => $chat_msg_text, "unread_messages_count" => $unread_messages_count, ]);
        exit();
    }
    usleep(10000);
    if ((time() - $time) > 60) {
        echo json_encode(["command" => "timeout"]);
        exit();
    }
 }

更新2: 我的托管服务提供商发送了一些有关它的信息,但我不明白它是什么...:

sendto(3, "\306\0\0\0\3SELECT * FROM messages LEFT JOIN users ON users.username=messages.chat_msg_user_id WHERE chat_msg_client_id='222' AND chat_msg_id>'571' AND chat_notification='0' ORDER BY chat_msg_time DESC LIMIT 1", 202, MSG_DONTWAIT, NULL, 0) = 202

更新3:我忘了说-每个用户有2个长轮询连接。一个用于获取新的聊天记录和新消息(用于通知),另一个用于在聊天时获取消息。

1 个答案:

答案 0 :(得分:1)

PHP通常使用线程池来处理请求。这种方法并没有真正支持将执行置于睡眠状态。您最终很快就会耗尽可用线程,并且由于所有线程都在休眠,因此新请求将不会得到处理。

您要么需要增加线程数量(无法扩展,而且托管提供商可能不支持),要么切换到其他方法(通过使用支持异步请求处理的语言/框架,例如NodeJS) )。

另请参阅an answer regarding long polling in PHP的摘录:

  

注意:在一个真实的站点上,在像Apache这样的常规网络服务器上运行它会迅速占用所有“工作线程”,并使它无法响应其他请求。建议使用Python的twisted之类的方式编写“长轮询服务器”,该服务器不依赖每个请求一个线程。 cometD是一种流行的语言(有多种语言可用),而Tornado是专门为此类任务制作的新框架(它是为FriendFeed的长轮询代码构建的)...但是作为一种简单的例子,A​​pache绰绰有余!这个脚本可以很容易地用任何一种语言编写(我选择了Apache / PHP,因为它们很常见,而我碰巧是在本地运行的)


更新

  

我的托管服务提供商说Apache与数据库的连接过多,导致服务器死亡。

如果这是问题所在,则可以通过进入睡眠see this for PDO之前关闭数据库连接来解决此问题(直到有关PHP线程的另一个问题接管为止)。

相关问题