HTTP प्रतिसाद भेजने के बाद php को संसाधित करना जारी रखें


101

मेरी स्क्रिप्ट सर्वर द्वारा कॉल की जाती है। सर्वर से मैं प्राप्त करेंगे ID_OF_MESSAGEऔर TEXT_OF_MESSAGE

अपनी स्क्रिप्ट में मैं आने वाले पाठ को संभालूँगा ANSWER_TO_IDऔर परमेस के साथ प्रतिक्रिया उत्पन्न करूँगा: और RESPONSE_MESSAGE

समस्या यह है कि मैं incomming के लिए प्रतिक्रिया भेज रहा हूं "ID_OF_MESSAGE", लेकिन सर्वर जो मुझे संदेश भेजने के लिए संभालता है वह अपना संदेश मेरे पास डिलीवर कर देगा (इसका मतलब है कि मैं उस आईडी पर प्रतिक्रिया भेज सकता हूं), http प्रतिक्रिया 200 प्राप्त करने के बाद।

समाधान में से एक संदेश को डेटाबेस में सहेजना और कुछ क्रोन करना है जो प्रत्येक मिनट चल रहा होगा, लेकिन मुझे तुरंत प्रतिक्रिया संदेश उत्पन्न करना होगा।

क्या कोई समाधान है कि सर्वर http प्रतिक्रिया 200 को कैसे भेजें और php स्क्रिप्ट निष्पादित करना जारी रखें?

आपका बहुत बहुत धन्यवाद

जवाबों:


191

हाँ। तुम यह केर सकते हो:

ignore_user_abort(true);
set_time_limit(0);

ob_start();
// do initial processing here
echo $response; // send the response
header('Connection: close');
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
flush();

// now the request is sent to the browser, but the script is still running
// so, you can continue...

5
क्या यह एक जीवित-जीवित कनेक्शन के साथ करना संभव है?
कांगेली ५०१

3
अति उत्कृष्ट!! इस सवाल का केवल यही जवाब है कि वास्तव में काम करता है !!! 10 पी +
मार्टिन_टेक

3
क्या आप ob_end_flush के बाद ob_flush का उपयोग करने का कोई कारण है? मैं अंत में वहाँ फ्लश फ़ंक्शन की आवश्यकता को समझता हूं, लेकिन मुझे यकीन नहीं है कि आपको ob_end_flush के साथ ob_flush की आवश्यकता क्यों होगी।
ars265

9
कृपया ध्यान दें कि यदि कोई सामग्री-एन्कोडिंग हेडर it कोई नहीं ’के अलावा किसी और चीज़ के लिए सेट है, तो यह इस उदाहरण को बेकार कर सकता है क्योंकि यह अभी भी उपयोगकर्ता को पूर्ण निष्पादन समय (समय समाप्त होने तक) प्रतीक्षा करने देगा। तो पूरी तरह से यकीन है कि यह उत्पादन के माहौल में स्थानीय काम करेगा, 'सामग्री-एन्कोडिंग' हेडर को 'कोई नहीं' सेट करें:header("Content-Encoding: none")
ब्रायन

17
युक्ति: मैंने PHP-FPM का उपयोग करना शुरू किया, इसलिए मुझे fastcgi_finish_request()अंत में जोड़ना पड़ा
vcampitelli

44

मैंने यहाँ बहुत सी प्रतिक्रियाएँ देखी हैं जो उपयोग करने का सुझाव देती हैं ignore_user_abort(true);लेकिन यह कोड आवश्यक नहीं है। यह सब सुनिश्चित करता है कि घटना में प्रतिक्रिया भेजे जाने से पहले आपकी स्क्रिप्ट निष्पादित हो रही है कि उपयोगकर्ता गर्भपात करता है (अनुरोध को रोकने के लिए अपने ब्राउज़र को बंद करके या भागने का दबाव डालकर)। लेकिन यह वह नहीं है जो आप पूछ रहे हैं। आप निष्पादन जारी रखने के लिए कह रहे हैं एक प्रतिक्रिया भेजी जाती है। आप सभी की जरूरत है निम्नलिखित है:

    // Buffer all upcoming output...
    ob_start();

    // Send your response.
    echo "Here be response";

    // Get the size of the output.
    $size = ob_get_length();

    // Disable compression (in case content length is compressed).
    header("Content-Encoding: none");

    // Set the content length of the response.
    header("Content-Length: {$size}");

    // Close the connection.
    header("Connection: close");

    // Flush all output.
    ob_end_flush();
    ob_flush();
    flush();

    // Close current session (if it exists).
    if(session_id()) session_write_close();

    // Start your background work here.
    ...

यदि आप चिंतित हैं कि आपका पृष्ठभूमि कार्य PHP की डिफ़ॉल्ट स्क्रिप्ट निष्पादन समय सीमा से अधिक समय लेगा, तो set_time_limit(0);शीर्ष पर रहें ।


3
विभिन्न संयोजनों की बहुत कोशिश की, यह वह है जो काम करता है !!! धन्यवाद Kosta Kontos !!!
मार्टिन_टेक

1
Apache 2, php 7.0.32 और ubuntu 16.04 पर पूरी तरह से काम करता है! धन्यवाद!
काइलबंगा

1
मैंने अन्य समाधानों की कोशिश की है, और केवल इसने मेरे लिए काम किया है। लाइनों का क्रम भी महत्वपूर्ण है।
सिनिसा

32

यदि आप FastCGI प्रसंस्करण या PHP-FPM का उपयोग कर रहे हैं, तो आप कर सकते हैं:

session_write_close(); //close the session
ignore_user_abort(true); //Prevent echo, print, and flush from killing the script
fastcgi_finish_request(); //this returns 200 to the user, and processing continues

// do desired processing ...
$expensiveCalulation = 1+1;
error_log($expensiveCalculation);

स्रोत: https://www.php.net/manual/en/function.fastcgi-finish-request.php

PHP समस्या # 68722: https://bugs.php.net/bug.php?id=68772


2
इसके लिए धन्यवाद, कुछ घंटे बिताने के बाद इसने मेरे लिए nginx में काम किया
एहसान

7
डाट सम महंगी गणना थ: ओ बहुत प्रभावित, इतनी महंगी!
फ्रेडरिक रोएल

धन्यवाद DarkNeuron! Php-fpm का उपयोग करके हमारे लिए शानदार उत्तर, बस मेरी समस्या हल हो गई!
सिनिसा

21

मैंने इस मुद्दे पर कुछ घंटे बिताए और मैं इस समारोह के साथ आया हूं जो अपाचे और नग्नेक्स पर काम करता है:

/**
 * respondOK.
 */
protected function respondOK()
{
    // check if fastcgi_finish_request is callable
    if (is_callable('fastcgi_finish_request')) {
        /*
         * This works in Nginx but the next approach not
         */
        session_write_close();
        fastcgi_finish_request();

        return;
    }

    ignore_user_abort(true);

    ob_start();
    $serverProtocole = filter_input(INPUT_SERVER, 'SERVER_PROTOCOL', FILTER_SANITIZE_STRING);
    header($serverProtocole.' 200 OK');
    header('Content-Encoding: none');
    header('Content-Length: '.ob_get_length());
    header('Connection: close');

    ob_end_flush();
    ob_flush();
    flush();
}

आप अपनी लंबी प्रोसेसिंग से पहले इस फ़ंक्शन को कॉल कर सकते हैं।


2
यह खूनी सुंदर है! ऊपर सब कुछ की कोशिश करने के बाद यह केवल एक चीज है जो नगीनेक्स के साथ काम करती है।
मसाले

आपका कोड लगभग यहां के कोड जैसा ही है, लेकिन आपकी पोस्ट पुरानी है :) +1
अकाउंटेंट م

filter_inputफ़ंक्शन के साथ सावधान रहें यह कभी-कभी पूर्ण रिटर्न देता है। विस्तार के लिए इस उपयोगकर्ता योगदान को देखें
अकाउंटेंट م

4

@Vcampitelli द्वारा उत्तर को थोड़ा संशोधित किया। लगता है कि आपको closeशीर्ष लेख की आवश्यकता नहीं है । मुझे क्रोम में डुप्लीकेट क्लोजर्स दिखाई दे रहे थे।

<?php

ignore_user_abort(true);

ob_start();
echo '{}';
header($_SERVER["SERVER_PROTOCOL"] . " 202 Accepted");
header("Status: 202 Accepted");
header("Content-Type: application/json");
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
flush();

sleep(10);

3
मैंने मूल उत्तर में इसका उल्लेख किया है, लेकिन मैं इसे यहां भी कहूंगा। आपको आवश्यक रूप से कनेक्शन बंद करने की आवश्यकता नहीं है, लेकिन फिर क्या होगा उसी कनेक्शन पर अनुरोध की गई अगली संपत्ति प्रतीक्षा करने के लिए मजबूर हो जाएगी। इसलिए आप HTML को तेज़ी से वितरित कर सकते हैं, लेकिन तब आपकी JS या CSS फाइलें धीरे-धीरे लोड हो सकती हैं, क्योंकि अगली परिसंपत्ति प्राप्त करने से पहले कनेक्शन को PHP से प्रतिक्रिया प्राप्त करना समाप्त करना होगा। तो उस कारण से, कनेक्शन को बंद करना एक अच्छा विचार है इसलिए ब्राउज़र को इसके मुक्त होने के लिए इंतजार नहीं करना पड़ता है।
नैट लैम्पटन

3

मैं इसके लिए php function register_shutdown_function का उपयोग करता हूं।

void register_shutdown_function ( callable $callback [, mixed $parameter [, mixed $... ]] )

http://php.net/manual/en/function.register-shutdown-function.php

संपादित करें : ऊपर काम नहीं कर रहा है। ऐसा लगता है कि मैं कुछ पुराने प्रलेखन द्वारा गुमराह किया गया था। PHP 4.1 लिंक लिंक के बाद से register_shutdown_function का व्यवहार बदल गया है


यह वह नहीं है जो पूछा जा रहा है - यह फ़ंक्शन मूल रूप से केवल स्क्रिप्ट समाप्ति घटना को बढ़ाता है और अभी भी आउटपुट बफर का हिस्सा है।
फिस्क

1
यह एक उत्थान के लायक पाया, क्योंकि यह दिखाता है कि क्या काम नहीं करता है।
ट्रेंडफिशर

1
Ditto - upvoting क्योंकि मैं इसे एक जवाब के रूप में देखने की उम्मीद कर रहा था, और यह देखने के लिए उपयोगी है कि यह काम नहीं करता है।
हैप्पीडॉग

2

मैं pthread स्थापित नहीं कर सकता और न ही पिछले समाधान मेरे लिए काम करते हैं। मुझे काम करने के लिए केवल निम्न समाधान मिला (Ref: https://stackoverflow.com/a/14469376/1315873 ):

<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort(); // optional
ob_start();
echo ('Text the user will see');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush(); // Strange behaviour, will not work
flush();            // Unless both are called !
session_write_close(); // Added a line suggested in the comment
// Do processing here 
sleep(30);
echo('Text user will never see');
?>

1

php file_get_contents उपयोग के मामले में, कनेक्शन बंद करना पर्याप्त नहीं है। php अभी भी सर्वर द्वारा ईओफ़ चुड़ैल भेजने के लिए प्रतीक्षा करें।

मेरा समाधान 'सामग्री-लंबाई:' पढ़ना है

यहाँ नमूना है:

response.php:

 <?php

ignore_user_abort(true);
set_time_limit(500);

ob_start();
echo 'ok'."\n";
header('Connection: close');
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
flush();
sleep(30);

पास लाइन के जवाब में "\ n" पर ध्यान दें, अगर ईओएफ प्रतीक्षा करते समय पढ़ा नहीं गया है।

read.php:

<?php
$vars = array(
    'hello' => 'world'
);
$content = http_build_query($vars);

fwrite($fp, "POST /response.php HTTP/1.1\r\n");
fwrite($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
fwrite($fp, "Content-Length: " . strlen($content) . "\r\n");
fwrite($fp, "Connection: close\r\n");
fwrite($fp, "\r\n");

fwrite($fp, $content);

$iSize = null;
$bHeaderEnd = false;
$sResponse = '';
do {
    $sTmp = fgets($fp, 1024);
    $iPos = strpos($sTmp, 'Content-Length: ');
    if ($iPos !== false) {
        $iSize = (int) substr($sTmp, strlen('Content-Length: '));
    }
    if ($bHeaderEnd) {
        $sResponse.= $sTmp;
    }
    if (strlen(trim($sTmp)) == 0) {
        $bHeaderEnd = true;
    }
} while (!feof($fp) && (is_null($iSize) || !is_null($iSize) && strlen($sResponse) < $iSize));
$result = trim($sResponse);

जैसा कि आप देख सकते हैं कि सामग्री की लंबाई तक पहुँचने पर यह स्क्रिप्ट ईओफ़ के बारे में इंतज़ार कर रही है।

आशा है कि यह मदद करेगा


1

मैंने इन लेखों का हवाला देते हुए अप्रैल 2012 में रासमस लेरडोर्फ से यह सवाल पूछा:

मैंने प्लेटफ़ॉर्म को सूचित करने के लिए एक नए PHP बिल्ट-इन फ़ंक्शन के विकास का सुझाव दिया था कि आगे कोई आउटपुट (स्टडआउट?) उत्पन्न नहीं होगा (इस तरह के फ़ंक्शन कनेक्शन को बंद करने का ध्यान रख सकता है)। रैसमस लेरडोर्फ ने जवाब दिया:

गियरमैन देखें । आप वास्तव में अपने फ्रंटएंड वेब सर्वर को इस तरह बैकएंड प्रोसेसिंग नहीं करना चाहते हैं।

मैं उनकी बात देख सकता हूं, और कुछ अनुप्रयोगों / लोडिंग परिदृश्यों के लिए उनकी राय का समर्थन कर सकता हूं! हालांकि, कुछ अन्य परिदृश्यों के तहत, vcampitelli et al के समाधान अच्छे हैं।


1

मेरे पास कुछ ऐसा है जो प्रतिक्रिया को संकुचित और भेज सकता है और अन्य php कोड को निष्पादित करने देता है।

function sendResponse($response){
    $contentencoding = 'none';
    if(ob_get_contents()){
        ob_end_clean();
        if(ob_get_contents()){
            ob_clean();
        }
    }
    header('Connection: close');
    header("cache-control: must-revalidate");
    header('Vary: Accept-Encoding');
    header('content-type: application/json; charset=utf-8');
    ob_start();
    if(phpversion()>='4.0.4pl1' && extension_loaded('zlib') && GZIP_ENABLED==1 && !empty($_SERVER["HTTP_ACCEPT_ENCODING"]) && (strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') !== false) && (strstr($GLOBALS['useragent'],'compatible') || strstr($GLOBALS['useragent'],'Gecko'))){
        $contentencoding = 'gzip';
        ob_start('ob_gzhandler');
    }
    header('Content-Encoding: '.$contentencoding);
    if (!empty($_GET['callback'])){
        echo $_GET['callback'].'('.$response.')';
    } else {
        echo $response;
    }
    if($contentencoding == 'gzip') {
        if(ob_get_contents()){
            ob_end_flush(); // Flush the output from ob_gzhandler
        }
    }
    header('Content-Length: '.ob_get_length());
    // flush all output
    if (ob_get_contents()){
        ob_end_flush(); // Flush the outer ob_start()
        if(ob_get_contents()){
            ob_flush();
        }
        flush();
    }
    if (session_id()) session_write_close();
}

0

एक और दृष्टिकोण और इसके सार्थक विचार है अगर आप प्रतिक्रिया हेडर के साथ छेड़छाड़ नहीं करना चाहते हैं। यदि आप किसी अन्य प्रक्रिया पर थ्रेड शुरू करते हैं, तो फंक्शन कहा जाता है, तो उसकी प्रतिक्रिया का इंतजार न करें और अंतिम रूप से http कोड के साथ ब्राउजर में वापस आ जाएं। आपको pthread को कॉन्फ़िगर करना होगा

class continue_processing_thread extends Thread 
{
     public function __construct($param1) 
     {
         $this->param1 = $param1
     }

     public function run() 
     {
        //Do your long running process here
     }
}

//This is your function called via an HTTP GET/POST etc
function rest_endpoint()
{
  //do whatever stuff needed by the response.

  //Create and start your thread. 
  //rest_endpoint wont wait for this to complete.
  $continue_processing = new continue_processing_thread($some_value);
  $continue_processing->start();

  echo json_encode($response)
}

एक बार जब हम $ जारी_ निष्पादित करें- > प्रारंभ () Continue_processing- PHP इस थ्रेड के रिटर्न रिजल्ट की प्रतीक्षा नहीं करता है और इसलिए जहाँ तक rest_endpoint माना जाता है। हो गया है।

कुछ लिंक pthreads के साथ मदद करने के लिए

सौभाग्य।


0

मुझे पता है कि यह एक पुराना है, लेकिन संभवतः इस बिंदु पर उपयोगी है।

इस उत्तर के साथ मैं वास्तविक प्रश्न का समर्थन नहीं करता लेकिन इस समस्या को कैसे हल करें। आशा है कि यह इस तरह की समस्याओं को हल करने में अन्य लोगों की मदद करता है।

मैं RabbitMQ या इसी तरह की सेवाओं का उपयोग करने और कार्यकर्ता उदाहरणों का उपयोग करके पृष्ठभूमि कार्यभार चलाने का सुझाव दूंगा । एक पैकेज है जिसे एमक्लिब कहा जाता है लिए जो आपके लिए RabbitMQ का उपयोग करने के लिए सभी काम करता है।

प्रो की:

  1. यह उच्च प्रदर्शन है
  2. अच्छी तरह से संरचित और बनाए रखने योग्य
  3. यह कार्यकर्ता उदाहरणों के साथ पूरी तरह से स्केलेबल है

Neg की:

  1. RabbitMQ को सर्वर पर स्थापित किया जाना चाहिए, यह कुछ वेब होस्ट के साथ समस्या हो सकती है।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.