Асинхронное программирование становится фундаментальным элементом в построении масштабируемых веб-приложений. Причина этого заключается в растущей потребности увеличения количества действий на каждый веб-запрос.Типичным примером этого является отправка электронного письма в рамках запроса.

Во многих веб-приложениях, когда что-то обрабатывается на сервере, мы хотим уведомить людей по электронной почте. Обычно для этого создается отдельный HTTP-запрос к стороннему сервису, такому как SendGrid, Mailchimp и т. д.

Совсем непросто, когда вам необходимо сразу отправить много электронных писем. Отправив десятки или сотни писем (HTTP-процесс при отправке одного письма занимает 100 мс) в PHP, вы сразу же увеличиваете общее время запроса. 

С другой стороны при решении этой проблемы, любой хороший сторонний почтовый сервис предоставит ендпоинт для отправки большого количества сообщений. Но предположим, что вы хотите отправить 100 сообщений, и каждое из них должно быть обработано отдельно.

Итак, нам нужно принять решение: как перенести обработку электронных писем в отдельный процесс, чтобы он не блокировал исходный веб-запрос?

Именно это мы и рассмотрим в данной статье, в частности, всевозможные способы решения данной проблемы на PHP используя новую инфраструктуру (или без нее).

Применение exec()

exec() — это нативная функция в PHP, которая может быть использована для выполнения внешней программы с возвратом результата. В нашем случае это скрипт, отправляющий электронную почту. Данная функция использует операционную систему для создания совершенно нового (пустой, ничего не копируется и не находится в общем доступе) процесса. Вы можете передать ему любое состояние, которое вам нужно.

Давайте рассмотрим пример.

<?php
// handle a web request

// record the start time of the web request
$start = microtime(true);
$path = __DIR__ . '/send_email.php';

// output to /dev/null & so we don't block to wait for the result
$command = 'php ' . $path . ' --email=%s > /dev/null &';
$emails = ['joe@blogs.com', 'jack@test.com'];

// for each of the emails, call exec to start a new script
foreach ($emails as $email) {
    // Execute the command
    exec(sprintf($command, $email));
}

// record the finish time of the web request
$finish = microtime(true);
$duration = round($finish - $start, 4);

// output duration of web request
echo "finished web request in $duration\n";

send_email.php

<?php

$email = explode('--email=', $argv[1])[1];
// this blocking sleep won't affect the web request duration
// (illustrative purposes only)
sleep(5);

// here we can send the email
echo "sending email to $email\n";

Вывод результата:

$ php src/exec.php

finished web request in 0.0184

Приведенные выше скрипты показывают, что веб-запрос по-прежнему завершается за миллисекунды, даже несмотря на блокирующий вызов функции sleep в send_email.php.

Причина, по которой он не блокируется, заключается в том, что мы сообщили exec с помощью включения > /dev/null & в команду о том, что не хотим ждать завершения команды exec для получения результата. Поэтому это произойдет в фоновом режиме, и веб-запрос может продолжиться.

Таким образом, скрипт веб-запроса просто отвечает за запуск сценария, а не за мониторинг его выполнения и/или сбоя.

Это неизбежный недостаток такого решения, поскольку мониторинг процесса возлагается на него самого, и он не может быть перезапущен. Тем не менее, это достаточно простой способ внедрить асинхронное поведение в PHP-приложение без особых усилий.

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

pcntl_fork

pcntl_fork — это низкоуровневая функция, требующая включения PCNTL-расширения и являющаяся мощным, но потенциально опасным методом при написании асинхронного кода в PHP.

pcntl_fork форкает или клонирует текущий процесс и разделяет его на родительский и несколько дочерних (в зависимости от того, сколько раз она будет вызвана). Определяя идентификатор процесса (Process ID или PID), мы можем выполнять различный код в контексте родительского или дочернего процесса.

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

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

Теперь перейдем к коду в нашем примере для неблокируемой отправки электронной почты.

<?php

function sendEmail($to, $subject, $message)
{
    // Code to send email (replace with your email sending logic)
    // This is just a mock implementation for demonstration purposes
    sleep(3); // Simulating sending email by sleeping for 3 seconds
    echo "Email sent to: $to\n";
}

$emails = [
    [
        'to' => 'john@example.com',
        'subject' => 'Hello John',
        'message' => 'This is a test email for John.',
    ],
    [
        'to' => 'jane@example.com',
        'subject' => 'Hello Jane',
        'message' => 'This is a test email for Jane.',
    ],
    // Add more email entries as needed
];

$children = [];

foreach ($emails as $email) {
    $pid = pcntl_fork();

    if ($pid == -1) {
        // Fork failed
        die('Error: Unable to fork process.');
    } elseif ($pid == 0) {
        // Child process
        sendEmail($email['to'], $email['subject'], $email['message']);
        exit(); // Exit the child process
    } else {
        // Parent process
        $children[] = $pid;
    }
}

echo "running some other things in parent process\n";
sleep(3);

// Parent process waits for each child process to finish
foreach ($children as $pid) {
    pcntl_waitpid($pid, $status);
    $status = pcntl_wexitstatus($status);
    echo "Child process $pid exited with status: $status\n";
}

echo 'All emails sent.';

В приведенном выше примере с помощью pcntl_fork мы можем форкнуть текущий процесс, который копирует родительский в новые дочерние процессы и дождаться завершения выполнения. Кроме того, после форкинга дочерних процессов для отправки электронной почты родительский процесс может продолжать выполнение других действий, до того как окончательно убедится, что дочерние процессы завершены.

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

Помимо этого, мы добиваемся изоляции процесса, поскольку каждый дочерний процесс выполняется в отдельной области памяти и не влияет на другие.

Отслеживая идентификаторы процессов, мы можем эффективно контролировать поток выполнения и управлять им.

Недостатком форкинга непосредственно из веб-запроса (родительского процесса), является то, что если ждать завершения дочерних процессов, то никакой выгоды по времени отклика на исходный запрос при таком способе не будет.

К счастью, есть решение, и оно заключается в том, чтобы объединить exec и pcntl_fork, для извлечения лучшего из обоих подходов. Это выглядит следующим образом:

  1. Веб-запрос использует exec() для создания нового PHP-процесса

  2. Созданному процессу передается список электронных писем в виде батча

  3. Созданный процесс становится родительским, поскольку он форкается для отправки каждого письма по отдельности

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

Давайте посмотрим, как это работает:

<?php

$start = microtime(true);
$path = __DIR__ . '/pcntl_fork_send_email.php';
$emails = implode(',', ['joe@blogs.com', 'jack@test.com']);
$command = 'php ' . $path . ' --emails=%s > /dev/null &';

// Execute the command
echo "running exec\n";
exec(sprintf($command, $emails));
$finish = microtime(true);

$duration = round($finish - $start, 4);
echo "finished web request in $duration\n";

pctnl_fork_send_email.php

<?php

$param = explode('--emails=', $argv[1])[1];
$emails = explode(',', $param);

function sendEmail($to)
{
    sleep(3); // Simulating sending email by sleeping for 3 seconds
    echo "Email sent to: $to\n";
}

$children = [];

foreach ($emails as $email) {
    $pid = pcntl_fork();

    if ($pid == -1) {
        // Fork failed
        die('Error: Unable to fork process.');
    } elseif ($pid == 0) {
        // Child process
        sendEmail($email);
        exit(); // Exit the child process
    } else {
        // Parent process
        $children[] = $pid;
    }
}

echo "running some other things in parent process\n";
sleep(3);

// Parent process waits for each child process to finish
foreach ($children as $pid) {
    pcntl_waitpid($pid, $status);
    $status = pcntl_wexitstatus($status);
    echo "Child process $pid exited with status: $status\n";
}

echo "All emails sent.\n";

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

AMPHP

amphp (Asynchronous Multi-tasking PHP. Асинхронный многозадачный PHP) — это набор библиотек, позволяющих создавать быстрые, параллельные приложения на PHP.

В релизе PHP 8.1 за ноябрь 2021 года появилась поддержка Fibers, реализующих легковесную кооперативную модель параллелизма.

Теперь мы знаем немного о том, как работает amphp и почему это интересно для будущих PHP-программ. Давайте рассмотрим пример:

<?php

require __DIR__ . '/../vendor/autoload.php'; // Include the autoload file for the amphp/amp library

use function Amp\delay;
use function Amp\async;

function sendEmail($to, $subject, $message)
{
    delay(3000)->onResolve(function () use ($to) {
        echo "Email sent to: $to\n";
    });
}

$emails = [
    [
        'to' => 'john@example.com',
        'subject' => 'Hello John',
        'message' => 'This is a test email for John.',
    ],
    [
        'to' => 'jane@example.com',
        'subject' => 'Hello Jane',
        'message' => 'This is a test email for Jane.',
    ],
    // Add more email entries as needed
];

foreach ($emails as $email) {
    $future = async(static function () use ($email) {
        $to = $email['to'];
        $subject = $email['subject'];
        $message = $email['message'];
        sendEmail($to, $subject, $message);
    });

    // block current process by running $future->await();
}

echo "All emails sent.\n";

Приведенный выше скрипт представляет собой очень простую версию асинхронного выполнения действий. Он создает новый файбер асинхронно, используя заданное замыкание, возвращая Future (объект).

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

Очереди и воркеры

Решение этой проблемы существует и за пределами PHP. До выхода PHP 8.1 его можно было считать золотым стандартом, поскольку оно не зависит от языка и обладает высокой масштабируемостью.

Использование сервисов очередей, таких как Amazon SQS, RabbitMQ или Apache Kafka, уже давно является общепринятым решением.

Очереди — это фрагменты инфраструктуры для запуска воркеров, независимых от вашего приложения, для асинхронной обработки любого задания. Это тоже не лишено риска и недостатков, но зато проверено временем.

Давайте разберем пример:

Отправитель, в данном случае, это, как правило, ваше существующее веб-приложение.

sender.php

<?php

require 'vendor/autoload.php';

use Aws\Sqs\SqsClient;

// Initialize the SQS client
$client = new SqsClient([
    'region' => 'us-east-1',
    'version' => 'latest',
    'credentials' => [
        'key' => 'YOUR_AWS_ACCESS_KEY',
        'secret' => 'YOUR_AWS_SECRET_ACCESS_KEY',
    ],
]);

// Define the message details
$message = [
    'to' => 'john@example.com',
    'subject' => 'Hello John',
    'message' => 'This is a test email for John.',
];

// Send the message to SQS
$result = $client->sendMessage([
    'QueueUrl' => 'YOUR_SQS_QUEUE_URL',
    'MessageBody' => json_encode($message),
]);

echo "Message sent to SQS with MessageId: " . $result['MessageId'] . "\n";

Воркеры — это дополнительный деплой исполняемого кода для обработки джобов.

worker.php

<?php

require 'vendor/autoload.php';

use Aws\Sqs\SqsClient;

// Initialize the SQS client
$client = new SqsClient([
    'region' => 'us-east-1',
    'version' => 'latest',
    'credentials' => [
        'key' => 'YOUR_AWS_ACCESS_KEY',
        'secret' => 'YOUR_AWS_SECRET_ACCESS_KEY',
    ],
]);

// Receive and process messages from SQS
while (true) {
    $result = $client->receiveMessage([
        'QueueUrl' => 'YOUR_SQS_QUEUE_URL',
        'MaxNumberOfMessages' => 1,
        'WaitTimeSeconds' => 20,
    ]);

    if (!empty($result['Messages'])) {
        foreach ($result['Messages'] as $message) {
            $body = json_decode($message['Body'], true);

            // Process the message (send email in this case)
            sendEmail($body['to'], $body['subject'], $body['message']);

            // Delete the message from SQS
            $client->deleteMessage([
                'QueueUrl' => 'YOUR_SQS_QUEUE_URL',
                'ReceiptHandle' => $message['ReceiptHandle'],
            ]);
        }
    }
}

function sendEmail($to, $subject, $message)
{
    sleep(3); // Simulating sending email by sleeping for 3 seconds
    echo "Email sent to: $to\n";
}

Это решение состоит из двух частей:

  • Отправитель (отправляет сообщение в SQS-очередь)

  • Воркер (получает сообщение из очереди и отправляет его по электронной почте).

Данное решение можно масштабировать путем увеличения количества воркеров по отношению к количеству сообщений, посылаемых любым количеством отправителей.

При использовании очереди воркер полностью независим от отправителя и может быть написан на любом языке, поскольку связь между отправителем и воркером осуществляется через JSON-сообщения.

Какое решение лучше?

Почти невозможно сказать, какое из решений, рассмотренных выше, будет лучшим для вашего приложения. Хотя все они направлены на обеспечение выполнения асинхронного кода в PHP, их реализация довольно сильно отличается и имеет различные преимущества и недостатки.

Обобщим каждый из вариантов в нескольких пунктах:

exec()

  • Возможно, самый простой и эффективный способ асинхронного выполнения PHP-скриптов.

  • Опасен потенциальными последствиями с точки зрения безопасности, особенно в отношении пользовательского ввода

  • Ничего не предоставляется для совместного использования, что может быть как благословением, так и проклятием

  • Может привести к повышенному потреблению ресурсов сервера (CPU/память)

pcntl_fork()

  • Позволяет управлять родительскими и дочерними процессами для кастомизации поведения

  • Может быть абстрагирован в более простой API для вашего приложения

  • Клонирование текущего процесса может вызвать появление других проблем, возникающих вследствие этого

AMPHP

  • Требуется PHP 8.1 для пользователя Fibers

  • Библиотека абстрагировалась от "жесткой сцепленности частей" выполнения асинхронного кода

  • Крутая кривая обучения по сравнению с другими более традиционными методами (понимание цикла событий и многозадачности в PHP)

Очереди и воркеры

  • Независимость от языка, гибкость для любого юзкейса

  • Представляет распределенную систему (в долгосрочной перспективе это может быть как хорошо, так и плохо)

  • Множество решений и различные провайдеры очередей облегчают работу.

Заключение

Основная причина, по которой я хотел немного углубиться в различные возможности асинхронного кода в PHP, заключается в том, чтобы понять, как введение Fibers в PHP 8.1 изменит перспективы для написания асинхронных программ в будущем.

Существует множество решений прошедших боевые испытания и не требующих PHP 8.1. Однако интересно посмотреть, в каком направлении развивается язык PHP, чтобы конкурировать с Golang и Elixir, которые поддерживают асинхронное программирование и делают это уже много лет.

В конечном счете, я, вероятно, все же бы выбрал подход на основе Queue/Worker (Очередь/Воркер), учитывая масштабируемость и кросс-платформенную/кросс-языковую поддержку. Однако думаю, со временем мы увидим, что такие библиотеки, как AMPHP, станут более функциональными и позволят решить эту проблему без внедрения новой инфраструктуры.

Примеры кода, использованные в этой статье, вы можете найти на GitHub.


Как написать библиотеку на C или Go для вашего PHP-проекта, а главное — зачем? Обсудим этот вопрос на открытом уроке 10 июля. На занятии поговорим о применении технологии, напишем библиотеку, и добавим ее в проект на PHP. Обсудим случаи применимости технологии FFI (Foreign Function Interface), поговорим о том, в каких случаях ее применять не стоит.

Занятие будет полезно для уверенно владеющих PHP разработчиков, которые пришли к вопросу о возможности встраивания низкоуровневых библиотек в свои проекты.

Комментарии (27)


  1. maledog
    03.07.2023 13:11

    Не очень знаком с PHP. Но мне кажется можно еще создать кольцевой буффер сообщений и форкнуть процесс отправки почты. Получится что-то вроде внутренней очереди.


  1. Ksoo
    03.07.2023 13:11

    Еще есть возможность отдать контент пользователю(fastcgi_finish_request())и продолжить выполнять работу.


    1. xtrime
      03.07.2023 13:11
      +1

      Да, если нагрузки не очень большие. В противном случае повышается риск того, что все fpm воркеры будут заняты и не смогут обрабатывать новые запросы.


      1. fear86
        03.07.2023 13:11
        +3

        Еще можно под это дело сделать отдельный fpm pool, что бы не блокировать основной. Так то этой мысли сто лет в обед https://tideways.com/profiler/blog/using-php-fpm-as-a-simple-built-in-async-queue


  1. koreychenko
    03.07.2023 13:11
    +2

    Кроме очередей все остальное выглядит как "смотри что я могу". Увидел бы такое в продакшене - повесил(ся) бы.

    Фишка в очередях ещё в том, что у вас отправитель и получатель сообщения могут быть написаны на разных языках. Например, навороченная бизнес-логика на PHP, а отправитель почты на go. Кроме того, для "разбора" очереди можно запустить кучу обработчиков параллельно.


  1. kirillbdev
    03.07.2023 13:11
    +2

    Ну пока что все "за" все еще говорят в пользу очередей. На мой взгляд самый главный и очевидный плюс (помимо того, что воркеры всякие могут быть реализованы на разных языках) - это контролируемая масштабируемость нагрзуки. Юзая exec и прочие подобные вещи, вы потеряете как минимум в контроле ресурсов.


  1. fear86
    03.07.2023 13:11

    Все эти exec и форки это еще тот хеппи-дебагинг будет, особенно под нагрузкой или по следам инцидента недельной давности. Если вы (или клиент) ленивый/нищий/жадный то просто используйте базу данных (или даже json файлы) + крон воркер(ы). Это потом всегда можно запихнуть в queue если что.


  1. kovserg
    03.07.2023 13:11

    $future = async(static function () use ($email) { ...

    $f1=function () {};
    $f2=static function () {};

    А в чем отличие между $f1 и $f2?


    1. Helldar
      03.07.2023 13:11

      Первый работает с динамической областью видимости, а второй со статической.

      Второй вариант меньше оперативки съест, но разница небольшая: бенчмарк в цикле по 1 ляму на каждый выдал, что для статики было выделено 376 метров памяти, а для динамики - 370.

       ------- --------------------------------- --------------------------------- 
        #       dynanyc                           static                          
       ------- --------------------------------- --------------------------------- 
        min     0.001 ms - 0 bytes                0.001 ms - 0 bytes               
        max     0.0461 ms - 0 bytes               0.0324 ms - 0 bytes              
        avg     0.0012 ms - 0 bytes               0.0012 ms - 0 bytes  
        total   5864.2512 ms - 376.00 MB          5927.6448 ms - 370.00 MB         
       ------- --------------------------------- --------------------------------- 
        Order   - 1 -                             - 2 -                            
       ------- --------------------------------- ---------------------------------
      $f1 = fn () => 1;
      $f2 = static fn () => 1;
      
      Benchmark::start()
          ->withoutData()
          ->iterations(1000000)
          ->compare([
              'dyn' => fn () => $f1(),
              'sta' => fn () => $f2(),
          ]);


      1. kovserg
        03.07.2023 13:11

        Хм, это измерение породило еще больше вопросов.
        А если dyn и sta поменять местами?


        ->compare([
                'dyn' => fn () => 1,
                'sta' => static fn () => 1,
            ]);

        ->compare([
                'sta' => static fn () => 1,
                'dyn' => fn () => 1,
            ]);


        1. Helldar
          03.07.2023 13:11

          Разницы нет. Бенчмарк проверяет каждый процесс в отдельности, включая память, затем суммирует для получения конечной информации.

          https://github.com/TheDragonCode/benchmark/blob/main/src/Services/Runner.php#L25-L36

          Можно даже упростить:

          Benchmark::start()
              ->withoutData()
              ->iterations(1000000)
              ->compare([
                  'dyn' => fn () => 1,
                  'sta' => static fn () => 1,
              ]);
           ------- ---------------------------------- -------------------------------- 
            #       dyn                                sta                             
           ------- ---------------------------------- -------------------------------- 
            min     0.0006 ms - 0 bytes                0.0005 ms - 0 bytes             
            max     2.7244 ms - 0 bytes                3.5528 ms - 0 bytes             
            avg     0.00099608599999764 ms - 0 bytes   0.000936866999999 ms - 0 bytes  
            total   6341.1074 ms - 376.00 MB           5566.6392 ms - 370.00 MB        
           ------- ---------------------------------- -------------------------------- 
            Order   - 2 -                              - 1 -                           
           ------- ---------------------------------- -------------------------------- 
           ------- --------------------------------- ---------------------------------- 
            #       sta                               dyn                               
           ------- --------------------------------- ---------------------------------- 
            min     0.0005 ms - 0 bytes               0.0006 ms - 0 bytes               
            max     0.1225 ms - 0 bytes               0.1353 ms - 0 bytes               
            avg     0.0008681610000013 ms - 0 bytes   0.00089107100000028 ms - 0 bytes  
            total   5376.6722 ms - 376.00 MB          6132.9579 ms - 370.00 MB          
           ------- --------------------------------- ---------------------------------- 
            Order   - 1 -                             - 2 -                             
           ------- --------------------------------- ---------------------------------- 

          Хотя, память как-то странно замеряется..

          Так или иначе, хоть где-то и читал что это экономит память, на деле получаем экономию на спичках.


          1. kovserg
            03.07.2023 13:11

            Кстати наикривейший benchmark если много итераций не используйте версию 2.5. (В 2.0 он не показывает это на публику)
            Размер 376Mb — это сколько сам бенчмар сожрал для вычисления средних значений. Это величина пропорциональна кол-ву итераций. А вот по времени отличия в рамках погрешности.


            ps: пришлось собрать php8 что бы это dragon-code/benchmark запустить.


            для обоих вариантов avg ~0.12us 
            
            А по итерациям:
            10000000 -  3.76 GB
            1000000 - 374.00 MB
            100000 - 38.00 MB
            10000 - 4.00 MB


            1. Helldar
              03.07.2023 13:11

              А Вы думали в 23-м году код на php 5.6 работать должен?)

              Состояние памяти запоминается перед стартом колбэка и сравнивается с полученным в конце. Единственное что в этом случае не учитывается, так это объём свойства класса, в которую сохраняются результаты. В версии 2.0 конечно память не показывал - в ней не было этой функциональной составляющей - отображение используемой памяти завезли в 2.1.

              Вы можете абсолютно любой бенчмарк сделать, хоть свой скрипт написать. Логика его работы банально проста и умещается в три строчки. Всё остальное - сахар:

              $startAt = hrtime(true);
              
              $callback();
              
              $timeDiff = (hrtime(true) - $startAt) / 1e+6;

              Если, по Вашему мнению, это наикривейший бенч, то где ссылка на наилучший? Только не говорите что он под капотом тоже будет использовать рекомендуемую PHP функцию hrtime и чтение состояния используемой памяти через memory_get_peak_usage или memory_get_usage.

              Вот на PHP 8.2 используемую память более реально можно будет отражать т.к. завезли функцию memory_reset_peak_usage. Апгрейд будет в рамках следующего мажора, а, пока что, текущего варианта за глаза хватает.


              1. kovserg
                03.07.2023 13:11

                А Вы думали в 23-м году код на php 5.6

                Просто в ubuntu 20.04 по умолчанию php7.4.3. Так что пришлось несколько раз собрать php-8.2.7. ( ./configure --with-openssl --enable-bcmath --with-curl )


                Anonymous functions may be declared statically. This prevents them from having the current class automatically bound to them. Objects may also not be bound to them at runtime.

                Так что походу этот static ни на что не влияет. И чуть более чем бесполезен.


                1. Helldar
                  03.07.2023 13:11

                  Жуть какая. Он официально не поддерживается.

                  Свежий пых ставится очень легко:

                  sudo apt update
                  sudo apt install lsb-release ca-certificates apt-transport-https software-properties-common -y
                  sudo add-apt-repository ppa:ondrej/php
                  sudo apt install php8.2


                  1. kovserg
                    03.07.2023 13:11

                    Жуть какая. Он официально не поддерживается.

                    И в чем же жуть?


                    1. Helldar
                      03.07.2023 13:11

                      Вроде современная ось, а репозиторий старый используется. PHP 7.4 снята с поддержки в ноябре 22-го года, а в ноябре 23-го снимется версия 8.0. То есть уже "в коробке" должна быть PHP 8.2, учитывая что в ноябре этого года обещают выпустить версию 8.3.


                      1. kovserg
                        03.07.2023 13:11

                        Вот я не понимаю зачем ставить php8.2 если он протухнет уже 2026?
                        Что за мода пошла на скоропортящиеся продукты.


                      1. Helldar
                        03.07.2023 13:11

                        Быстрее развиваются?


                      1. kovserg
                        03.07.2023 13:11

                        Развиваются куда? В ubuntu 14.04 были поддерживались разные кодировки, в 20.04 уже нет. В windows7 можно было панель задач разместить на разных сторонах экрана, в win11 это надо делать через реестр. Если раньше numpy работал на процессорах без avx, то теперь нет.


                      1. Helldar
                        03.07.2023 13:11

                        Значит функции стали невостребованными или устаревшими. Либо разрабы посчитали их таковыми.


                      1. kovserg
                        03.07.2023 13:11

                        разрабы посчитали их таковыми

                        Не уверен что это зависит от разработчиков.


                      1. Helldar
                        03.07.2023 13:11

                        Да как сказать. Сомневаюсь что за реализацию продвижения фич в том же PHP отвечают маркетологи, например...

                        Они реализуют своё видение продукта, также публикуют RFC с некоторыми моментами, далее собирают фидбэк от других разработчиков по всему миру и смотрят что их заинтересует, что наоборот разгневает, а до чего будет до лампочки и, на основании этого, планируют разработку в том числе.

                        Технологии для разработчиков разрабатывают разработчики, как бы это ни звучало. А вот в какой области этот продукт будет использоваться - это уже другой вопрос.

                        Но, опять же, если взять тот же Windows, то они явно хотят изменить интерфейс и улучшить продукт, и делают это. А отключая функции также реагируют на потребителей. На днях краем глаза прочитал что в "проводнике" какую-то фичу хотели вырезать, типа ненужная, но именно разработчики накинулись на них и закидали тапками, те сдались и не стали вырезать. Особо не вникал в ситуацию, гуглится не сложно, если что.


                      1. kovserg
                        03.07.2023 13:11

                        они явно хотят изменить интерфейс и улучшить продукт

                        Они явно коммерческая организация и явно хотят больше прибыли. Для этого надо что бы что-то менялось. А что бы не сидели на "старом" надо вилами подгонять (вносить изменения так что бы они немножко ломали обратную совместимость, что бы поддерживать старые системы становилось не рентабельным и становилось не комфортным для обычных пользователей оставаться на старой ос, дабы побудить их к обновлению и постепенному переходу на подписку, что бы конечно же упростить жизнь пользователю, делая упор на безопасность, комфорт и красивое оформление дизайн обоев).


                      1. Helldar
                        03.07.2023 13:11

                        То что одна из основных целей проекта - его капитализация, не секрет.

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

                        Так что причины для такого можно найти и обосновать. Но в случае с Microsoft - капитализация 100%.


  1. FelixTheMagnificent
    03.07.2023 13:11

    Смотришь на это все и вспоминаешь, как еще в 2012 году был Sidekiq у RoR, который делал все то же самое, использовал Redis в качестве message bus, и был легок в отладке, в случае чего.


    1. fear86
      03.07.2023 13:11
      +1

      И в php этому всему миллионы лет (тот же gearman). Дело в том что PHP ассинхронный by design (многопоточный скорей), в отличии от тех же руби или js (который ассинхронный но не многопоточный). По этому в 99% случаев не нужна никакая ассинхронность. Обычно она нужна как раз в случаях выполнения реально долгоиграющей работы (например рассылка 100500 писем). Так же с этим в 99% случаев справляется cronjob + примитивная очередь в базе данных. И только в 0.01% случаев нужен MQ. А с MQ все как у всех, Kafka, Rabbit, Redis, DBA, и тд.