Pull to refresh
313.55
Skyeng
Крутейшая edtech-команда страны. Удаленная работа

Асинхронный PHP. Зачем?

Reading time7 min
Views28K
Original author: Sergey Zhuk
image

Асинхронное программирование сегодня востребовано, особенно в веб-разработке, где отзывчивость приложения особенно важна. Никому не хочется ждать, пока приложение «отвиснет», пусть даже в это время оно выполняет запрос к базе данных, отправляет электронное письмо или работает над другими задачами, которые могут занять много времени. Клиент хочет получить ответ на свое действие, лучше всего — сразу же. Если ваше приложение работает медленно, вы теряете клиентов. Столкнувшись с зависающим приложением, чаще всего пользователь просто закрывает его и уже никогда не возвращается. С точки зрения пользователя, приложение просто зависло, он не может понять, почему это произошло: выполняет ли оно сложную операцию или перестало работать в принципе.

Представляем перевод статьи бэкенд-разработчика Skyeng Сергея Жука.

Отзывчивость приложения


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

Параллелизм и асинхронность


Когда люди видят асинхронный код, чаще всего они сразу же думают: «Отлично, я могу распараллелить процессы!». Возможно, я вас разочарую, но на самом деле это не так. Асинхронность и параллелизм — не одно и то же. Уловить эту разницу может быть непросто, поэтому давайте попробуем разобраться.

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

Асинхронность:
Идите и выполните задачу самостоятельно. Дайте мне знать, когда закончите, и покажите результат. В это время я могу продолжать заниматься своей задачей.

Асинхронный код требует обработки зависимостей, возникающих при выполнении задач, и это реализуется с помощью колбэков. Когда какая-то задача выполнена, код уведомляет об этом другую задачу. Асинхронный код в основном — это про время выполнения задачи (порядок событий).
image
Параллелизм:
Наймите столько работников, сколько хотите, и разделите задачу между ними, чтобы быстрее выполнить ее, и уведомите меня, когда вы закончите. Я могу продолжить заниматься другими делами или, если задача срочная, я останусь и подожду, пока вы вернетесь с результатами. Затем я смогу скомпоновать итоговый результат от всех работников. Параллельное выполнение часто требует больше ресурсов, поэтому здесь в основном все зависит от железа.

image
Чтобы понять разницу между асинхронным и параллельным выполнением на реальном примере, сравним два популярных веб-сервера: Apache и Nginx. Они прекрасно иллюстрируют это различие. Nginx использует асинхронную модель на основе событий в то время как Apache задействует параллельные потоки. Apache создает новые потоки для каждого дополнительного соединения, поэтому здесь максимальное число допустимых соединений зависит от объема доступной памяти в системе. Когда предел подключений достигнут, Apache перестает создавать дополнительные подключения. Ограничивающим фактором при настройке Apache является память (помните, что параллельное выполнение часто зависит от железа). Если поток останавливается, клиент ожидает ответа, пока поток не освободится и не сможет отправить ответ.

Nginx работает не так, как Apache, — он не создает новых потоков для каждого входящего запроса. У Nginx есть основной рабочий процесс (или несколько воркеров, зачастую для одного процессора имеется один рабочий процесс ), который является однопоточным. Этот воркер может обрабатывать тысячи соединений «одновременно» и делает это асинхронно, одним потоком, а не параллельно в несколько потоков.

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

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

А что от этого бэкенду?


Вы можете сказать, что на бэкенде отзывчивость не так уж и важна. Все эти крутые асинхронные штуки с JavaScript происходят на фротенде, а ваш сервер просто отвечает на запросы, поэтому за отзывчивость приложения должен отвечать фротенд, но никак не вы. Да, это правда, но задача сервера не ограничивается только ответами API. Иногда приходится управлять сложными задачами, например, загрузкой видео. В этом случае, возможно, отзывчивость не является ключевым фактором, но ее отсутствие приводит к расходованию ресурсов впустую, потому что приложению приходится простаивать. Оно может ожидать завершения операций файловой системы, сетевого взаимодействия, запросов к базе данных и тому подобное. Часто эти операции ввода-вывода выполняются очень медленно по сравнению с вычислениями процессора, например когда мы конвертируем видеофайлы. И пока мы медленно сохраняем или читаем файл, процессор вынужден находиться в режиме ожидания вместо того, чтобы выполнять полезную работу. Как мы уже выяснили, вместо ожидания мы можем запускать эти задачи в фоновом режиме. Как? Идем дальше.

Асинхронный PHP


В JavaScript прямо из коробки доступна встроенная поддержка и решения для написания асинхронного кода. Также есть Node.js, который позволяет писать асинхронные серверные приложения. В JavaScript мы можем использовать функцию setTimeout () для демонстрации примера асинхронного кода:
image

При запуске этого кода мы увидим следующее:
image

Функция setTimeout () отправляет код в очередь и выполнит его по завершению текущего стека вызовов. Это означает, что мы нарушаем синхронный поток кода и задерживаем выполнение. Второй вызов console.log () выполняется перед вызовом в очереди внутри вызова setTimeout ().

Что же насчет PHP? В PHP у нас нет подходящих адаптированных инструментов для написания по-настоящему асинхронного кода. Не существует эквивалента setTimeout (), и мы не можем просто отложить или поставить в очередь выполнение какого-либо кода. Вот почему стали появляться такие фреймворки и библиотеки, как Amp и ReactPHP. Их идея заключается в том, чтобы скрыть низкоуровневые детали языка и дать пользователю инструменты высокого уровня, которые позволяют писать асинхронный код и управлять конкурентным выполнением процессов, как JavaScript и Node.js.

Зачем мне использовать PHP, если есть Node.js и Go?


Этот вопрос часто возникает, когда речь идет об асинхронном PHP. Почему-то многие выступают против использования PHP для написания асинхронного кода. Кто-то всегда предлагает вместо PHP использовать Go или Node.js.

Твит assertchris прекрасно описывает такие ситуации:

image

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

Да, вы можете использовать Go или Node.js для создания асинхронных серверных приложений, но это не всегда решает проблему. Если у вас уже есть большой опыт работы с PHP, вам будет гораздо проще просто изучить библиотеки и инструменты, подходящие для вашей ситуации, чем изучать новый язык и новую экосистему. Такие инструменты, как ReactPHP или Amp, позволяют писать асинхронный код так же, как вы делаете это в Node.js. Эти инструменты достаточно развиты и имеют стабильные версии, поэтому вы можете безопасно использовать их в продакшене.

Не только CLI


Я не собираюсь писать чат, сервер или что-то подобное. Я просто хочу ускорить работу сайта.

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

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

Да, асинхронный код в большинстве случаев все еще используется в сценариях CLI, но он не ограничивается real-time чатами и серверами. Если вы просто хотите ускорить работу своего сайта, вам не нужно отказываться от вашего фреймворка Symfony или Laravel и создавать свой собственный полностью асинхронный сервер приложений.

Заключение


Не бойтесь изучать новую парадигму. PHP – это гораздо больше, чем «запусти скрипт, выполни код и умри». Вы удивитесь, когда поймете, что можно использовать знакомый вам PHP совершенно по-новому, так, как вы никогда его не использовали! Асинхронный код и событийно-ориентированное программирование расширит ваши представления о PHP и возможности использования этого языка. Не нужно изучать новый язык для написания асинхронных приложений только потому, что «PHP — неподходящий инструмент» или «я всегда так делал, его невозможно улучшить». Просто попробуйте!

Ну а также напоминаем, что у нас всегда много интересных вакансий для разработчиков!
Tags:
Hubs:
+17
Comments18

Articles

Information

Website
www.skyeng.team
Registered
Founded
Employees
1,001–5,000 employees
Location
Россия
Representative
Alisa Kruglova