Laravel CRON или процесс события отвечают на запрос API через длинный опрос - как оживить сеанс

У меня есть маршрут poll на API на сервере Laravel 5.7, где пользователь api может запрашивать любую информацию с момента последнего опроса.

Легкая часть - немедленно ответить на действительный запрос, если есть новая информация return $this->prepareResult($newData);.

Если новых данных нет, я сохраняю запрос опроса в базе данных, и утилита cron может затем раз в минуту проверять все запросы опроса и отвечать на любые опросы, в которых данные были обновлены. В качестве альтернативы я могу создать прослушиватель событий для обновления данных и запускать ответ на опрос при обновлении данных.

Я застрял в том, как восстановить каждый сеанс, чтобы он соответствовал устройству, ожидающему обновления. Я могу сохранить или передать идентификатор сеанса, но как мне убедиться, что обработчик задач / событий CRON может ответить на правильный IP-адрес так же, как если бы он был на исходный запрос. Может ли php это сделать?

Я пытаюсь избегать веб-сокетов, так как у меня будет много устройств, но с ограниченными обновлениями / взаимодействиями.

веб-сокеты в php блокируют процесс ... но использование решения на основе узлов не имеет этого ограничения, и socket.io может делать то, что вы хотите :)

Antony Gibbs 02.11.2018 10:46
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
8
1
992
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Клиенты опрашивают обновления, API не отправляют обновления.

Предполагается, что REST API не должны иметь состояния, поэтому попытка отслеживания серверной части идет вразрез с REST.
Чтобы конкретно ответить на ваш вопрос, если вы не хотите использовать веб-сокеты, клиентское приложение должно будет продолжать опрашивать конечную точку до тех пор, пока не станут доступны данные.

Длинный опрос является допустимой техникой, и для этого конкретного варианта использования будет хорошо работать. Методы, которые я видел в Laravel, имеют тенденцию сохранять сеанс открытым (sleep), а затем отвечать в будущем, что немного расточительно.

brianlmerritt 24.10.2018 11:00

Long polling требует держать соединение открытым. Это может произойти только через бесконечный цикл checking, чтобы увидеть, существуют ли данные, а затем добавить sleep.

Нет необходимости оживлять сеанс, поскольку ответ запускается только при успешном попадании данных.

Обратите внимание, что этот метод очень интенсивен для ЦП и памяти, поскольку соединение и рабочий FPM будут оставаться открытыми до успешного попадания данных. Веб-сокеты - гораздо лучшее решение независимо от количества устройств и частоты обновлений.

Вы можете использовать уведомления. «уведомление браузера» для веб-клиентов и уведомление FCM и APN для мобильных клиентов.

Другой вариант - использовать SSE (события, отправленные сервером). Это соединение как сокет, но через http. Клиент отправляет обычный запрос, и сервер может просто отвечать клиенту несколько раз и в любое время, если клиент доступен (в том же запросе, который был отправлен).

Спасибо - я считаю, что SSE требует продолжения сеанса, что снова связывает ресурсы сервера. Посмотрим на FCM и посмотрим, может ли приложение Ionic это использовать (есть драйверы для Laravel).

brianlmerritt 30.10.2018 14:47
Ответ принят как подходящий

Длинный опрос - действенный прием. Я считаю, что проводить опрос с сессией - плохая идея. поскольку сеанс предназначен только для исходного пользователя. вы можете запустить свой длинный опрос с помощью php cli. вы можете проверить свое промежуточное программное обеспечение, чтобы разрешить cli только для опроса маршрута. вы можете использовать pthreads для запуска длинного опроса используйте pthreads через cli. и теперь pthreads v3 разработан безопасно и разумно везде, кроме интерфейса командной строки. вы можете использовать свой cron для запуска вашего потока каждый час. тогда в вашем контроллере вам нужно сохранить $ time = time (); чтобы отметить время начала исполнения. затем создайте цикл dowhile, чтобы зациклить процесс опроса. while может быть ($ time> time () + 3600) или другим условием. внутри цикла вам нужно проверить, существует ли опрос? если это правда, запустите его. затем в нижней части строки внутри цикла вам нужно поспать на какую-то секунду, например 2 секунды.

на вашем background.php (этот файл выполняется cron)

<?php
error_reporting(-1);
ini_set('display_errors', 1);

class Atomic extends Threaded {
    public function __construct($data = NULL) {
        $this->data = $data;
    }

    private $data;
    private $method;
    private $class;
    private $config;
}

class Task extends Thread {

    public function __construct(Atomic $atomic) {
        $this->atomic = $atomic;
    }

    public function run() {
        $this->atomic->synchronized(function($atomic) 
        {
            chdir($atomic->config['root']);

            $exec_statement = array(
                "php7.2.7",
                $atomic->config['index'],
                $atomic->class,
                $atomic->method
            );

            echo "Running Command".PHP_EOL. implode(" ", $exec_statement)." at: ".date("Y-m-d H:i:s").PHP_EOL;
            $data = shell_exec(implode(" ", $exec_statement));

            echo $data.PHP_EOL;

        }, $this->atomic);
    }

    private $atomic;
}

$config = array(
    "root" => "/var/www/api.example.com/api/v1.1",
    "index" => "index.php",
    "interval_execution_time" => 200
);

chdir($config['root']);

$threads = array();


$list_threads = array(
    array(
        "class" => "Background_workers",
        "method" => "send_email",
        "total_thread" => 2
    ),
    array(
        "class" => "Background_workers",
        "method" => "updating_data_user",
        "total_thread" => 2
    ),
    array(
        "class" => "Background_workers",
        "method" => "sending_fcm_broadcast",
        "total_thread" => 2
    )
);

for ($i=0; $i < count($list_threads); $i++) 
{
    $total_thread = $list_threads[$i]['total_thread'];

    for ($j=0; $j < $total_thread; $j++) 
    { 
        $atomic = new Atomic();
        $atomic->class = $list_threads[$i]['class'];
        $atomic->method = $list_threads[$i]['method'];
        $atomic->thread_number = $j;
        $atomic->config = $config;

        $threads[] = new Task($atomic);
    }
}

foreach ($threads as $thread) {
    $thread->start();
    usleep(200);
}

foreach ($threads as $thread)
    $thread->join();
?>

и это на вашем контроллере

<?php
    defined('BASEPATH') OR exit('No direct script access allowed');

    class Background_workers extends MX_Controller {

        public function __construct()
        {
            parent::__construct();
            $this->load->database();
            $this->output->enable_profiler(FALSE);
            $this->configuration = $this->config->item("configuration_background_worker_module");
        }

        public function sending_fcm_broadcast() {
            $time_run = time();
            $time_stop = strtotime("+1 hour");

            do{
                $time_run = time();

                modules::run("Background_worker_module/sending_fcm_broadcast", $this->configuration["fcm_broadcast"]["limit"]);

                sleep(2);
            }
            while ($time_run < $time_stop);
        }
    }

это пример кода запуска из контроллера codeigniter.

Другие вопросы по теме