У меня есть маршрут poll на API на сервере Laravel 5.7, где пользователь api может запрашивать любую информацию с момента последнего опроса.
Легкая часть - немедленно ответить на действительный запрос, если есть новая информация return $this->prepareResult($newData);.
Если новых данных нет, я сохраняю запрос опроса в базе данных, и утилита cron может затем раз в минуту проверять все запросы опроса и отвечать на любые опросы, в которых данные были обновлены. В качестве альтернативы я могу создать прослушиватель событий для обновления данных и запускать ответ на опрос при обновлении данных.
Я застрял в том, как восстановить каждый сеанс, чтобы он соответствовал устройству, ожидающему обновления. Я могу сохранить или передать идентификатор сеанса, но как мне убедиться, что обработчик задач / событий CRON может ответить на правильный IP-адрес так же, как если бы он был на исходный запрос. Может ли php это сделать?
Я пытаюсь избегать веб-сокетов, так как у меня будет много устройств, но с ограниченными обновлениями / взаимодействиями.






Клиенты опрашивают обновления, API не отправляют обновления.
Предполагается, что REST API не должны иметь состояния, поэтому попытка отслеживания серверной части идет вразрез с REST.
Чтобы конкретно ответить на ваш вопрос, если вы не хотите использовать веб-сокеты, клиентское приложение должно будет продолжать опрашивать конечную точку до тех пор, пока не станут доступны данные.
Длинный опрос является допустимой техникой, и для этого конкретного варианта использования будет хорошо работать. Методы, которые я видел в Laravel, имеют тенденцию сохранять сеанс открытым (sleep), а затем отвечать в будущем, что немного расточительно.
Long polling требует держать соединение открытым. Это может произойти только через бесконечный цикл checking, чтобы увидеть, существуют ли данные, а затем добавить sleep.
Нет необходимости оживлять сеанс, поскольку ответ запускается только при успешном попадании данных.
Обратите внимание, что этот метод очень интенсивен для ЦП и памяти, поскольку соединение и рабочий FPM будут оставаться открытыми до успешного попадания данных. Веб-сокеты - гораздо лучшее решение независимо от количества устройств и частоты обновлений.
Вы можете использовать уведомления. «уведомление браузера» для веб-клиентов и уведомление FCM и APN для мобильных клиентов.
Другой вариант - использовать SSE (события, отправленные сервером). Это соединение как сокет, но через http. Клиент отправляет обычный запрос, и сервер может просто отвечать клиенту несколько раз и в любое время, если клиент доступен (в том же запросе, который был отправлен).
Спасибо - я считаю, что SSE требует продолжения сеанса, что снова связывает ресурсы сервера. Посмотрим на FCM и посмотрим, может ли приложение Ionic это использовать (есть драйверы для Laravel).
Длинный опрос - действенный прием. Я считаю, что проводить опрос с сессией - плохая идея. поскольку сеанс предназначен только для исходного пользователя. вы можете запустить свой длинный опрос с помощью 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.
веб-сокеты в php блокируют процесс ... но использование решения на основе узлов не имеет этого ограничения, и socket.io может делать то, что вы хотите :)