We show the process of running a continuous task on the server using one connection

    I needed to make the interactive execution of the script work to the user. I have implemented a multi-threaded PHP bot performing a background task receiving requests for execution. He writes the results of his activities to the database. Next, I needed to somehow inform the user about the execution process.

    Usually, in this case, for example, they make a long request, at the end of which a response is sent from the server about the current state, after which they repeat again, or check the status every couple of seconds. But I wanted to implement this more lively, using one connection.

    The resulting method can be used to continuously receive a response from the server and process it in parallel without interruption. That is, you can not only use it to obtain the state of readiness of the process, but also you can, for example, process huge data in the process of receiving it, without waiting for it to be fully downloaded.

    How will we act?


    The method is to force the PHP script to send data to the client in predetermined portions. And in receiving these servings on the client side.

    Unfortunately, many browsers do not know how to properly process the data transfer state in AJAX, so we will conditionally synchronize the data in seconds. We will reset the data from the server every 2 seconds, and check the arrival of new data on the client side every 2 seconds. In this case, inconsistencies often occur, some data came already newer than needed, or there is no new data yet. I will take into account only the first case, because it is assumed that your ping is high enough to ensure that the data is in excess.

    We force the server to give data


    To do this, we need to prohibit all compression methods, tell the browser that we are transmitting the stream, and force the user to reset all output data.

    Also, one of the most points is to close the record of an open session. The fact is that PHP cannot open the same session in two parallel threads, so if your process works and you make a parallel request, it will hang in the queue. As a result, all AJAX features will be paralyzed.

    header('Content-Encoding: none;'); //вырубаем сжатие
    header('Content-type: application/octet-stream');
    // выключаем буферизацию
    ini_set('output_buffering', 'off');
    // выключаем сжатие со стороны php
    ini_set('zlib.output_compression', false);
    // включаем сброс данных в браузер после каждого вывода
    ini_set('implicit_flush', true);
    ob_implicit_flush(true);
    // закрываем сессию на запись, после этой команды нельзя будет записывать данные в сессию (!)
    session_write_close();
    for ($i = 0;$i < 20; $i++) {
        echo '|'.$i.str_repeat(' ', 4096).PHP_EOL;
        flush();
        sleep(2);
    }
    

    Above, I use the | separator to display only the latest data and split the transmitted data into logical parts.

    Note - we hammer in the extra 4096 bytes to force the packet to be sent to the user. Otherwise, the web server will cache the data until it is collected in the packet necessary for sending. Perhaps there is a method to send without it, I will be ready to listen to the solutions in the comments.

    The script above will transmit numbers from 0 to 19 every 2 seconds. The browser will only display the latest data.

    Client part


    var xhr;
    var timerlive;
    function live(cont){
        try{
            xhr.abort();
        }catch(e) {
         }
        var temp;
        clearTimeout(timerlive);
        var prevtext = '';
        xhr = new XMLHttpRequest();
        xhr.open('POST', 'ajax.php', true);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.send("type=live");
        timerlive = setInterval(function() {
            if (xhr.readyState == 4) {
                clearTimeout(timerlive);
            }
            temp=xhr.responseText.substring(prevtext.length);
            $('#'+cont).html(temp.substr(temp.lastIndexOf("|")+1));
            prevtext =  xhr.responseText;
        }, 2000);
    }
    function breaklive(){
        try{
            xhr.abort();
        }catch(e) {
         }
        clearTimeout(timerlive);
    }
    


    The call to the live () function will first interrupt the previous live channel and then initiate a new connection to the server. We will record the old data in order to know the starting point for the new data. Also using a separator | we will finish only the latest data from the received. In general, you can simply separate only the latest data, but you can process all the new data if, for example, an online log is generated.

    There is a function for interrupting the connection below, it can be embedded in your AJAX scripts so that when you switch to another page, the connection is closed.

    Only registered users can participate in the survey. Please come in.

    How do you notify the user about the status of a task?

    • 7.4% I use a method similar to the method from article 4
    • 48.1% I check the status through periods of time, making individual requests 26
    • 33.3% Notify the user only of the completion of the process using Long-Pooling 18
    • 27.7% I don’t notify the user at all, it will be necessary, he will refresh the page and see it. fifteen

    Also popular now: