【问题标题】:Ajax call blocks the server until it finishesAjax 调用阻塞服务器直到它完成
【发布时间】:2015-08-15 21:41:49
【问题描述】:

我有一个论坛(使用 PHP 和 Symfony2 框架),我想要一些非常接近实时通知的东西。我更具体地想要的是,当用户收到私人消息时,会出现通知。我尝试使用长轮询技术,但遇到了问题。

所以,我从 javascrip 向 php 发送了一个 ajax 调用。 PHP 接收到它并查询数据库以查看是否有任何新的通知给 ajax 请求的用户。如果它找到某些东西,它会返回该结果,否则会休眠 x 秒。这是在一段时间(真)条件下发生的。问题是,如果它没有找到任何东西,它就会休眠,然后再次搜索,然后再次休眠,以此类推,而当这种情况发生时,其他任何东西都不起作用。如果我尝试重新加载页面,单击论坛中的某些内容,阅读主题等等,所有这些都被阻止等待该 ajax 调用完成。我该怎么做才能使 ajax 调用与其他所有内容并行运行,或者在 ajax 未完成时解除对服务器的阻塞。

这就是我在 javascript 中所拥有的:

function poll() {
    $.ajax({
        type: "post",
        url: url to the action from controller,
        async: true,
        data: { 'userId': $('#userId').attr('data-value') },
        dataType: "json",
        success: function(result) {
            call method to display the notification(s) to the user
            setTimeout(poll, 5000);
        }
    });
};

$(document).ready(function(){
    poll();
});

这是来自 Symfony2 控制器的操作:

public function checkForNewNotificationsAction($userId) {
    while (true) {
        /** @var \Forum\CoreBundle\Entity\Notification[] $notifications */
        $notifications = query the database

        if ($notifications != null) {
            return new JsonResponse($notifications);
        } else {
            sleep(5);
        }
    }
}

我还尝试使用“纯”PHP 代码调用常规 PHP 文件,认为来自控制器的操作以及 Symfony2 如何管理控制器正在阻止我的其他活动,但它完全一样。

我试图不使用任何其他“工具”,例如 Node.js 和 Socket.io 或类似的东西,因为我从未使用过它们,而且真的没有时间学习。该项目必须在几天内完成,还有很多工作要做。此外,我试图避免每 x 秒调用一次简单的 ajax 调用,将此作为我的最后选择。

【问题讨论】:

  • 你用什么服务器?
  • 我使用 Apache 服务器。
  • 您的选择是:1) 客户端短轮询,2) 客户端长轮询,3) 与 webSocket 的持续连接。
  • 客户端短轮询和长轮询有什么区别?它们的真正含义是什么?

标签: javascript php jquery ajax symfony


【解决方案1】:

这是很自然的,因为脚本正在获取会话锁定。
在 Ajax 操作的开头添加一个session_write_close(); 以便释放锁。你基本上要做的是告诉 php,你不会再写入会话所以释放锁,这样可以处理并发请求.注意不要在进行此调用后写入会话。 官方来源:session_write_close()

更新:

symfony 的SessionInterface 有一个save 方法,该方法又调用session_write_close(),你应该使用它而不是session_write_close。例如:

$request->getSession()->save();

当你想再次写入会话时,你应该调用SessionInterface::start

【讨论】:

  • 我已经试过了。将 session_write_close() 放在我的函数头之后的第一行,while(true) 条件的第一行,if 的第一行或 else 的第一行。没有任何作用,它完全一样。
  • 这很奇怪,因为这是一个常见问题,而这始终是 AFIK 的解决方案。检查这些:konrness.com/php5/how-to-prevent-blocking-php-requestsstackoverflow.com/questions/12401358/…。你认为还有什么东西可以锁定某物吗?
  • 我能想到的只是 Symfony 在管理控制器操作时做了更多的事情,这可能是导致我的问题的原因(或者可能是我没有尝试的 session_start() )。我将尝试在那个“纯”PHP 脚本中使用 session_write_close() 并让您知道发生了什么。但我今晚会试试这个,现在我无法访问那个项目。
  • OK。仅供参考,我正在使用这种与 symfony 2.5 完全相同的方法来显示进度条。
  • 我使用的是 Symfony 2.7.3,但现在如果你说它适合你,那很可能不是 Symfony 的错。您是使用 session_start() 手动启动会话,然后在需要时关闭它,还是关闭它让会话自动启动?
【解决方案2】:

如果让 PHP 立即返回并将 JavaScript 更改为类似的内容会怎样?至少你可以做点什么。

function poll() {
    $.ajax({
        type: "post",
        url: url to the action from controller,
        async: true,
        data: { 'userId': $('#userId').attr('data-value') },
        dataType: "json",
        success: function(result) {
            //call method to display the notification(s) to the user
            //if (problem) {pollForever = false;}
        }
    });
};

var pollForever;

$(document).ready(function(){
    pollForever = true;

    while (pollForever) {
        setTimeout(poll, 5000);
    }
});

【讨论】:

  • 正如我所说,这是我最后的选择。我不希望客户端不断检查通知,我希望服务器自己做,只有在有新消息时才响应。
【解决方案3】:

试试这个。

var myvar = setInterval(function () {checkForNewNotificationsAction($userId)}, 5);

public function checkForNewNotificationsAction($userId) {
   while (true) {
    /** @var \Forum\CoreBundle\Entity\Notification[] $notifications */
    $notifications = query the database

    if ($notifications != null) {
        clearInterval(myVar);
        return new JsonResponse($notifications);
    }
  } 
}

【讨论】:

  • 它会一遍又一遍地休眠,直到出现通知,这正是我想要的。如果我返回一些东西,它会响应 ajax,我不想要这个。我想要更少的 ajax 调用,让服务器只在它确实有东西可以提供给客户端时才响应。
  • 我已经修改过了。是你想要的还是类似的?你必须把 un $userId 你想要的参数。查看此页面了解更多信息:w3schools.com/js/js_timing.asp
【解决方案4】:

“解决”这个问题的“快速而肮脏的方式”:

您可以这样做:

  • 创建一个名为 dbchanged.txt 的文件,内容为“0”
  • 当您更改数据库中的某些内容时,您会更改该文件“1”的内容。
  • 当您从 ajax 发出请求时,您检查 dbchanged.txt 的内容
    • 如果 dbchanged.txt 的内容为“1”,则执行 db-request。此请求完成后,将 dbchanged.txt 内容更改为“0”,然后返回。
    • 如果 dbchanged.txt 的内容为“0”,则直接返回而不执行任何 db-request。

为什么我说“解决”这个问题是因为它实际上并没有解决问题,但它会使其更快,因为它(通常)读取文件比访问数据库更快。另一方面,您可能不得不面对文件锁定问题 - 取决于我们讨论的请求数量。

做正确的事!

没有解决问题的“快速方法”,我真的建议您阅读 long pulliing request 和 websockets。使用 sockets.IO 似乎并不难,而且是一种比上面更好的解决方案 - 以下是创建聊天应用程序的方法(并且很容易理解代码中的内容) :http://socket.io/get-started/chat/

HTML5 websockets 并非在所有浏览器中都可用,但 socketIO(使用 html5 websockets)会为您处理这些问题,并在 websockets 不可用时使用 long pull-technique。

jquery 和 socketIO 的示例: https://gist.github.com/anandgeorge/2814934

另一个可能有帮助的参考: http://techoctave.com/c7/posts/60-simple-long-polling-example-with-javascript-and-jquery

【讨论】:

  • 你给我-1,请告诉我为什么!
猜你喜欢
  • 1970-01-01
  • 2016-05-31
  • 1970-01-01
  • 1970-01-01
  • 2021-07-30
  • 2010-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多