Программирование

Как быстро вызвать curl в PHP из 1.php без ожидания ответа

В 1.php можно отправить запрос к 2.php через cURL, сразу закрыв соединение и не дожидаясь ответа, используя опции CURLOPT_RETURNTRANSFER=false, Connection: close.

Как вызвать curl в PHP и не ждать ответа от удалённого скрипта?
У меня есть скрипт 1.php, который делает запрос к 2.php через curl.
Скрипт 2.php может выполняться долго (например, sleep(10)).
Я хочу, чтобы скрипт 1.php сразу выводил “1-ok” и завершался, не дожидаясь завершения 2.php.
Как настроить curl или использовать асинхронный запрос, чтобы это было возможно?

Скрипт 1.php может «отправить» запрос к 2.php, сразу закрывая соединение и не дожидаясь его завершения.
Нужно заставить cURL «отправить» HTTP‑запрос, затем закрыть сокет до получения ответа. Это достигается комбинацией опций, которые отключают ожидание ответа и заставляют сервер держать соединение открытым.


Содержание


Краткое решение

php
$ch = curl_init('http://example.com/2.php');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);   // не хранить ответ
curl_setopt($ch, CURLOPT_HEADER, false);           // без заголовков
curl_setopt($ch, CURLOPT_NOSIGNAL, 1);             // не использовать сигналы
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 1);           // очень короткое ожидание
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Connection: close']);
curl_exec($ch);   // отправляем, но не ждём ответа
curl_close($ch);

echo "1-ok";

Connection: close говорит серверу, что клиент сразу разорвёт соединение, но сервер продолжит работу скрипта 2.php.


Подключаем cURL и задаём параметры

Параметр Что делает Почему важно
CURLOPT_RETURNTRANSFER = false Не сохранять ответ в переменную Уменьшает нагрузку
CURLOPT_HEADER = false Не получать заголовки Уменьшает объём данных
CURLOPT_NOSIGNAL = true Отключить сигналы (не блокирует) Не вызывает таймауты
CURLOPT_TIMEOUT_MS = 1 Минимальный таймаут в миллисекундах Позволяет быстро завершить curl_exec
CURLOPT_CONNECTTIMEOUT_MS = 1 Минимальный таймаут подключения Быстрое завершение при проблемах
CURLOPT_HTTPHEADER = ['Connection: close'] Сообщаем серверу об отключении Сервер продолжит работу в фоне

Информация об опциях: PHP Manual – curl_setopt.


Пошаговый пример кода

php
<?php
// 1.php
$url = 'http://example.com/2.php'; // адрес 2.php

$ch = curl_init($url);

// Отключаем ожидание ответа
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_NOSIGNAL, true);

// Минимальный таймаут, чтобы curl «забросил» запрос и закрыл соединение
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 1);

// Указываем, что соединение будет закрыто клиентом
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Connection: close']);

// Отправляем запрос
curl_exec($ch);
curl_close($ch);

// Немедленный вывод
echo '1-ok';
?>

Когда 1.php будет выполнен, сервер 2.php уже получил запрос и продолжит работу (например sleep(10)), но клиент уже получил ответ и завершил скрипт.


Альтернативы: fsockopen и Guzzle

1. fsockopen

php
$fp = fsockopen('example.com', 80, $errno, $errstr, 1);
if ($fp) {
    fwrite($fp, "GET /2.php HTTP/1.1\r\n");
    fwrite($fp, "Host: example.com\r\n");
    fwrite($fp, "Connection: close\r\n\r\n");
    fclose($fp); // закрываем сразу, не читаем ответ
}
echo '1-ok';

fsockopen открывает низкоуровневый сокет, отправляет raw HTTP‑запрос и сразу закрывает соединение. Это самый «чистый» способ fire‑and‑forget.

2. Guzzle (асинхронный запрос)

php
$client = new \GuzzleHttp\Client();
$promise = $client->requestAsync('GET', 'http://example.com/2.php');
$promise->then(
    fn($res) => null,   // ничего не делаем при ответе
    fn($e) => null      // игнорируем ошибки
);
// Не ждём, просто продолжаем выполнение
echo '1-ok';

Guzzle поддерживает асинхронные запросы, но по‑прежнему держит соединение открытым до завершения. Для настоящего fire‑and‑forget лучше использовать fsockopen или описанный выше cURL‑вариант.


Пояснения к опциям

  • Connection: close
    HTTP‑заголовок, который сообщает серверу, что клиент не будет держать соединение открытым. Это ключ к тому, что сервер будет продолжать работу после того, как клиент разорвет соединение.
  • CURLOPT_TIMEOUT_MS / CURLOPT_CONNECTTIMEOUT_MS
    Устанавливают минимальный таймаут в миллисекундах. Если задать 1, curl_exec почти мгновенно вернёт управление, даже если сервер ещё обрабатывает запрос.
  • CURLOPT_NOSIGNAL
    Отключает использование сигналов, которые могут блокировать curl_exec при таймауте в некоторых версиях PHP.

Заключение

  1. С помощью cURL: задайте Connection: close, минимальные таймауты и отключите возврат данных. После curl_exec сразу закройте дескриптор и выводите 1-ok.
  2. Если нужно более простое решение: используйте fsockopen и посылайте raw HTTP‑запрос.
  3. Асинхронные библиотеки (например, Guzzle) не гарантируют fire‑and‑forget; они ждут завершения соединения, поэтому для настоящего «отправить и забыть» предпочтительнее cURL‑вариант выше.

Эти методы позволяют 1.php завершиться мгновенно, а 2.php продолжать работу в фоновом режиме, даже если это sleep(10) или более длительная операция.


Источники

  1. PHP Manual – curl_setopt
  2. Stack Overflow – PHP cURL required only to send and not wait for response
  3. Stack Overflow – PHP Fire and Forget POST Request
  4. Stack Overflow – Creating Fire‑and‑Forget requests in PHP
  5. GitHub – hipsterjazzbo/fire-and-forget
Авторы
Проверено модерацией
Модерация