Обработка медленных запросов
Иногда нам необходимо обрабатывать медленные запросы, и чтобы избежать влияния этих медленных процессов на другие запросы, для них можно использовать различные подходы в зависимости от ситуации.
Подход 1 Использование очереди сообщений
Обратитесь к очередям Redis и очередям Stomp
Преимущества
Позволяет справляться с мгновенными массовыми запросами на обработку.
Недостатки
Невозможно напрямую вернуть результат клиенту. Если необходимо отправить результат, потребуется сочетание с другими сервисами, например, используя webman/push для отправки результата обработки.
Подход 2 Добавление HTTP порта
Добавление нового HTTP порта для обработки медленных запросов; эти медленные запросы обрабатываются конкретной группой процессов при доступе к этому порту, после чего результат возвращается клиенту.
Преимущества
Можно напрямую вернуть данные клиенту.
Недостатки
Невозможно справляться с внезапной массовой нагрузкой запросов.
Шаги по реализации
В config/process.php
добавьте следующую конфигурацию.
return [
// ... здесь опущены другие настройки ...
'task' => [
'handler' => \Webman\App::class,
'listen' => 'http://0.0.0.0:8686',
'count' => 8, // число процессов
'user' => '',
'group' => '',
'reusePort' => true,
'constructor' => [
'requestClass' => \support\Request::class, // настройка класса request
'logger' => \support\Log::channel('default'), // экземпляр лога
'appPath' => app_path(), // путь к каталогу app
'publicPath' => public_path() // путь к каталогу public
]
]
];
Таким образом, медленные интерфейсы могут проходить через процессы по адресу http://127.0.0.1:8686/
, не влияя на обработку других процессов.
Чтобы обеспечить, чтобы фронтенд не ощущал различий между портами, можно добавить в nginx прокси на порт 8686. Предположим, что все пути запросов медленного интерфейса начинаются с /tast
, общая конфигурация nginx будет выглядеть примерно так:
upstream webman {
server 127.0.0.1:8787;
keepalive 10240;
}
# Добавляем новый 8686 upstream
upstream task {
server 127.0.0.1:8686;
keepalive 10240;
}
server {
server_name webman.com;
listen 80;
access_log off;
root /path/webman/public;
# Запросы, начинающиеся с /tast, идут на порт 8686; измените /tast на нужный вам префикс
location /tast {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://task;
}
# Другие запросы идут через оригинальный порт 8787
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Connection "";
if (!-f $request_filename){
proxy_pass http://webman;
}
}
}
Таким образом, когда клиент обращается к domain.com/tast/xxx
, запрос будет обрабатываться на отдельном порту 8686, не влияя на обработку запросов на порту 8787.
Подход 3 Использование HTTP chunked для асинхронной отправки данных по частям
Преимущества
Можно напрямую вернуть данные клиенту.
Установка workerman/http-client
composer require workerman/http-client
app/controller/IndexController.php
<?php
namespace app\controller;
use support\Request;
use support\Response;
use Workerman\Protocols\Http\Chunk;
class IndexController
{
public function index(Request $request)
{
$connection = $request->connection;
$http = new \Workerman\Http\Client();
$http->get('https://example.com/', function ($response) use ($connection) {
$connection->send(new Chunk($response->getBody()));
$connection->send(new Chunk('')); // отправка пустого chunk для завершения response
});
// сначала отправляем http заголовок, а последующие данные отправляются асинхронно
return response()->withHeaders([
"Transfer-Encoding" => "chunked",
]);
}
}
Совет
В этом примере используетсяworkerman/http-client
для асинхронного получения HTTP результата и возврата данных; также можно использовать другие асинхронные клиенты, такие как AsyncTcpConnection.