简体   繁体   English

是否可以同时从PHP服务器接收多个AJAX响应?

[英]Is there a possibility to receive several AJAX responses from PHP server simultaneously?

I want to make a progress bar on my website, which tracks execution of a PHP script. 我想在我的网站上创建一个进度条,该进度条跟踪PHP脚本的执行。

The PHP script makes a bunch of connections with Google API and stores the data it receives in the database. PHP脚本与Google API建立了许多连接,并将其接收的数据存储在数据库中。 Sometimes the process can take a minute. 有时,此过程可能需要一分钟。

The PHP script is located in ajax/integrations-ajax.php file and launched by GET AJAX request sent, if on the website to click #link button. 该PHP脚本位于ajax/integrations-ajax.php文件中,并通过GET AJAX请求发送启动,如果在网站上单击#link按钮。 Below is jQuery code for the request: 以下是该请求的jQuery代码:

$('#link').on('click', function () {
    var interval = setInterval(trackStatus, 1000);
    $.getJSON('ajax/integrations-ajax.php', {action: 'link'}).done(function (json) {            
        if (json.result == true) {
            showMessage('The account is successfully linked.', 'success');
        } else {
            showMessage('There is an error happened.', 'danger');
        }
    })
});

This #link button, also sets interval which fires trackStatus function each second: #link按钮还设置间隔,该间隔会trackStatus触发trackStatus函数:

function trackStatus() {
    $.getJSON('ajax/status-ajax.php', { 
        action: 'status'
    }).done(function (json) {
        console.log(json.status);
    });
}

As you can see, trackStatus function sends GET AJAX requests to ajax/status-ajax.php file and should show status in browser console every second. 如您所见, trackStatus函数将GET AJAX请求发送到ajax/status-ajax.php文件,并且应该每秒在浏览器控制台中显示状态。

To implement tracking ability on the server I made the PHP script in ajax/integrations-ajax.php file to store status in the database. 为了在服务器上实现跟踪功能,我在ajax/integrations-ajax.php文件中制作了PHP脚本,以将状态存储在数据库中。 Its code you can see below: 您可以在下面看到其代码:

<?php
if(!is_ajax_request()) { exit; }
$action = isset($_GET['action']) ? (string) $_GET['action'] : '';
if ($action == 'link') {
    set_status_in_database(0);
    // some execution code;

    set_status_in_database(1);
    // some execution code;

    set_status_in_database(2);
    // some execution code;

    set_status_in_database(3);
    // some execution code;
    echo json_encode(['result' => true ]);
}

And created another PHP file axax/status-ajax.php which can recover the status from the database: 并创建了另一个PHP文件axax/status-ajax.php ,该文件可以从数据库中恢复状态:

<?php
if(!is_ajax_request()) { exit; }
$action = isset($_GET['action']) ? (string) $_GET['action'] : '';
if ($action == 'status') {
    $return['result'] = get_status_from_database();
    echo json_encode($return);
}

But the requests appear not to be working simultaneously. 但是这些请求似乎无法同时工作。 I can't receive responses for trackStatus function until the response on completion ajax/integrations-ajax.php script isn't received. 在收到关于完成ajax/integrations-ajax.php脚本的响应之前,我无法收到trackStatus函数的响应。

I made a profiling record in browser, which show that: 我在浏览器中进行了性能分析,记录显示: 在此处输入图片说明

So, is there a possibility to execute requests simultaneously? 那么,是否有可能同时执行请求? Or to implement the tracking ability I need to rethink the whole approach? 还是要实现跟踪功能,我需要重新考虑整个方法?

Thanks in advance for help! 在此先感谢您的帮助!


Update Thank you all for your advice! 更新谢谢大家的建议! And especially to @Keith, because his solution is the easiest and works. 特别是@Keith,因为他的解决方案最简单且有效。 I have put session_write_close() function in the beginning for the script and everything works: 我在脚本的开头放置了session_write_close()函数,一切正常:

<?php
if(!is_ajax_request()) { exit; }
$action = isset($_GET['action']) ? (string) $_GET['action'] : '';
if ($action == 'link') {
    session_write_close();
    set_status_in_database(0);
    // some execution code;

    set_status_in_database(1);
    // some execution code;

    set_status_in_database(2);
    // some execution code;

    set_status_in_database(3);
    // some execution code;
    echo json_encode(['result' => true ]);
}

Here you can see profiling record from a browser: 在这里,您可以查看来自浏览器的分析记录: 在此处输入图片说明

Would advise trying EventSource . 建议尝试使用EventSource Here is an example. 这是一个例子。

PHP 的PHP

<?php
header('Content-Type: text/event-stream');
// recommended to prevent caching of event data.
header('Cache-Control: no-cache'); 

function send_message($id, $message, $progress) {
  $d = array('message' => $message , 'progress' => $progress);
  echo "id: $id" . PHP_EOL;
  echo "data: " . json_encode($d) . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}

for($i=0; $i<4; $i++){
  set_status_in_database($i);
  // some execution code;
  send_message($i, "set status in database " . $i + 1 . " of 3' , $i*4);
  sleep(1);
}

send_message('CLOSE', 'Process complete');
?>

JavaScript 的JavaScript

var es;

function startTask() {
  es = new eventSource('ajax/status-ajax.php');
  es.addEventListener('message', function(e) {
    var result = JSON.parse(e.data);
    console.log(result.message);
    if(e.lastEventId == 'CLOSE') {
      console.log('Received CLOSE closing');
      es.close();
      showMessage('The account is successfully linked.', 'success');
    } else {
      $('.progress').css("width", result.progress + '%');
    }
  });
  es.addEventListener('error', function(e) {
    console.log('Error occurred', e);
    es.close();
  });
}

function stopTask() {
  es.close();
  console.log('Interrupted');
}

$('#link').on('click', function(e) {
  e.preventDefault();
  startTask($(this));
});

Reference: 参考:

Hope that is useful for you. 希望对您有用。

While PHP can handle concurrent requests without issue, one area that does get serialized is the Session, basically PHP during a request will place an exclusive lock on the SESSION, for that user. 虽然PHP可以毫无问题地处理并发请求,但确实可以序列化的一个区域是Session,基本上,在请求期间,PHP会为该用户在SESSION上设置排他锁。 IOW: While this lock is on, other requests from the same user will have to wait. IOW:当此锁打开时,来自同一用户的其他请求将不得不等待。 This is normally not an issue, but if you have long running requests it will block other requests, like AJax requests etc. 通常这不是问题,但是如果您有长时间运行的请求,它将阻止其他请求,例如AJax请求等。

As a default PHP will write session data at then end off the request,. 默认情况下,PHP将在写入会话数据,然后结束请求。 But if you are sure you no longer need to write any session data, calling session_write_close will release the lock much sooner. 但是,如果您确定不再需要写入任何会话数据,则调用session_write_close将更快地释放该锁。

More info here -> http://php.net/manual/en/function.session-write-close.php 更多信息在这里-> http://php.net/manual/en/function.session-write-close.php

Both php logic and JavaScript syntax seem to be fine; php逻辑和JavaScript语法似乎都不错; however, with the minimal amount of php code example it is assumed that it's resource heavy. 但是,以最少的php代码示例为例,它假定资源很繁重。 MySQL might be busy, which is why get status may wait for MySQL. MySQL可能很忙,这就是为什么获取状态可能要等待MySQL。

I have gone around such a problem by making the update status written to a file instead of competing for database resources. 通过将更新状态 写入文件而不是争用数据库资源,我解决了这个问题。

Since you consider using a different approach, let me recommend GraphQL as a thin layer / api above your database. 由于您考虑使用其他方法,因此让我推荐GraphQL作为数据库之上的薄层/ api。

There are quite a few Php-solutions out there, for example Siler . 那里有很多Php解决方案,例如Siler Look for one that has subscriptions (not all do), as this would be the feature you are looking for. 寻找一个有订阅 (不是全部)的人,因为这是您要寻找的功能。 Subscriptions are used to create a websocket (stream between your Php and Javascript), reducing all status-related communication to one call. 订阅用于创建Websocket(Php和Javascript之间的流),从而将与状态相关的所有通信减少到一个呼叫。

Yes, this may be "shooting cannons at birds", but maybe you have other things flying around, then it might be worth considering. 是的,这可能是“向大炮射击”,但也许您还有其他东西在飞来飞去,那么也许值得考虑。 There is a fantastic document to familiarize with the intriguing concept. 有一个很棒的文档可以熟悉这个有趣的概念。 You'd be able to reuse most of your database-related Php within the resolver functions . 您将能够在resolver函数内重用大多数与数据库相关的Php。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM