Параллельный ввод-вывод всегда был технической проблемой в программировании на стороне сервера, начиная с самого раннего процесса прямого разветвления с синхронной блокировкой, до пула рабочих процессов/пула потоков, до текущего асинхронного ввода-вывода и совместного ввода-вывода. Поскольку PHP-программисты имеют сильную платформу lamp, они мало что знают об этом виде базовых знаний. Цель этой статьи-подробно представить различные попытки программирования параллельного ввода-вывода на PHP и, наконец , ввести использование swool, чтобы всесторонне проанализировать проблему параллельного ввода-вывода.
Блокировка многопроцессорной/многопоточной синхронизации
Самые ранние серверные программы решали проблему одновременного ввода-вывода с помощью нескольких процессов и потоков. Появилась самая ранняя модель процесса, концепция процесса существовала с момента рождения системы UNIX. Самая ранняя серверная программа, как правило, принимает клиентское соединение для создания процесса, а затем подпроцесс входит в блок циклической синхронизации для взаимодействия с клиентским соединением, получения и обработки данных.
Режим многопоточности появится позже. Потоки легче процессов, и потоки совместно используют стек памяти, поэтому взаимодействие между различными потоками очень легко достигается. Например, такая программа, как чат-комната, может взаимодействовать между клиентскими подключениями и отправлять сообщения любому другому лицу, кроме игроков в чате. Очень просто использовать режим многопоточности, в котором данные могут быть отправлены непосредственно в клиентское соединение. Многопроцессный режим требует конвейера, очереди сообщений и общей памяти, что может быть реализовано с помощью комплексной технологии межпроцессной связи (IPC).
Экземпляр кода:
Процесс многопроцессной/потоковой модели является
- Создайте сокет , свяжите порт сервера и порт прослушивания. В PHP вы можете использовать функцию сервера потоковых сокетов для выполнения вышеуказанных трех шагов. Конечно, вы также можете использовать расширение сокетов на более низком уровне для их реализации соответственно.
- Войдите в цикл while, заблокируйте операцию принятия и дождитесь ввода клиентского соединения. В это время программа будет переходить в спящий режим до тех пор, пока новый клиент не начнет подключаться к серверу, и операционная система не разбудит процесс. Функция accept возвращает сокет клиентского подключения
- Основной процесс создает подпроцессы с помощью fork (PHP: pcntl_fork) в модели с несколькими процессами и pthread_create (PHP: Новый поток). Если ниже нет специального объявления, процессы будут использоваться для одновременного представления процессов/потоков.
- После успешного создания подпроцесса он входит в цикл while и блокируется в recv (PHP: При вызове дождитесь, пока клиент отправит данные на сервер. После получения данных серверная программа обрабатывает их, а затем с помощью send (PHP: Fwrite) отправляет ответ клиенту. Служба с длительным подключением будет продолжать взаимодействовать с клиентом, в то время как служба с коротким подключением, как правило, закроется после получения ответа.
- Когда клиентское соединение закрывается, подпроцесс завершается и уничтожает все ресурсы. Основной процесс переработает этот дочерний процесс.
Самая большая проблема с этим шаблоном заключается в том, что создание и уничтожение процесса/потока обходятся дорого. Таким образом, приведенный выше шаблон не может быть применен к очень загруженным серверным программам. Соответствующая улучшенная версия решает эту проблему, которая является классической моделью лидера-последователя.
Экземпляр кода:
Его особенность заключается в том, что после запуска программы будет создано n процессов. Каждый подпроцесс вводит “принять”, ожидая ввода нового соединения. Когда клиент подключается к серверу, один из подпроцессов будет активирован, чтобы начать обработку запроса клиента и больше не принимать новое TCP – соединение. Когда это соединение будет закрыто, подпроцесс будет освобожден и повторно введите “Принять” для участия в обработке нового соединения.
Преимущество этой модели заключается в том, что процесс можно полностью использовать повторно, без дополнительного потребления, а производительность очень хорошая. Многие распространенные серверные программы основаны на этой модели, такие как Apache, php-fpm.
Многопроцессорная модель также имеет некоторые недостатки.
- Эта модель в значительной степени зависит от количества процессов для решения проблемы параллелизма. Клиентское соединение должно занимать процесс, а количество рабочих процессов и производительность обработки параллелизма одинаковы. Количество процессов, которые может создать операционная система, ограничено.
- Запуск большого количества процессов приведет к дополнительному потреблению ресурсов для планирования процессов. Когда есть сотни процессов, потребление планирования переключения контекста процесса составляет менее 1% от ЦП, что можно игнорировать. Если будут запущены тысячи или даже десятки тысяч процессов, потребление будет расти по прямой линии. Потребление по расписанию может составлять Десятки или даже 100% процессора.
Кроме того, существуют некоторые сценарии, которые не могут быть решены с помощью модели с несколькими процессами, такие как обмен мгновенными сообщениями (IM), серверу необходимо поддерживать десятки тысяч или даже сотни тысяч миллионов подключений одновременно (классическая проблема C10K), а модели с несколькими процессами недостаточно.
Другой сценарий также является слабостью модели с несколькими процессами. Как правило, веб-сервер запускает 100 процессов. Если запрос занимает 100 мс, 100 процессов могут обеспечить 1000 QPS, поэтому производительность обработки хорошая. Но если внутренний запрос вызывает внешний HTTP-интерфейс, такой как QQ и логин Weibo, это займет много времени. Для запроса требуется 10 секунд. Этот процесс может обрабатывать только 0,1 запроса за одну секунду, а 100 процессов могут достигать только 10 qps, что слишком мало.
Существует ли технология, которая может обрабатывать все одновременные операции ввода-вывода в одном процессе? Ответ-да, это технология мультиплексирования ввода-вывода.
Мультиплексирование ввода-вывода/циклирование событий/асинхронное неблокирующее
На самом деле история повторного использования ввода-вывода такая же длинная, как и история нескольких процессов. Linux уже давно предоставляет системные вызовы select, которые могут поддерживать 1024 соединения в процессе. Позже был добавлен системный вызов опроса, и опрос внес некоторые улучшения, чтобы решить проблему ограничения 1024, которое может поддерживать любое количество подключений. Но еще одна проблема с select/poll заключается в том, что ему необходимо выполнить цикл, чтобы проверить, есть ли события в соединении. В этом-то и проблема. Если сервер имеет 1 миллион подключений и только одно соединение отправляет данные на сервер в определенное время, для выбора/опроса необходимо выполнить 1 миллион циклов, только один из которых выполнен, а остальные 999999 недействительны, что приводит к потере ресурсов процессора.
До тех пор, пока ядро Linux 2.6 не предоставит новый системный вызов epoll, который может поддерживать неограниченное количество подключений и не требует опроса, это действительно может решить проблему C10K. Теперь на основе epoll реализованы все виды высокопроизводительных асинхронных серверных программ ввода-вывода, таких как nginx, node.js, Эрланг и голанг. Один процесс и однопоточные программы, такие как node.js может поддерживать более 1 миллиона TCP-соединений, и все это благодаря технологии epoll.
Асинхронная неблокирующая программа мультиплексирования ввода-вывода использует классическую модель реактора, что означает реактор, как следует из названия. Он не обрабатывает передачу каких-либо данных. Вы можете отслеживать только изменения событий дескриптора сокета.
Реактор имеет четыре основных режима работы:
- Добавить добавить сокет для прослушивания реактора, который может быть прослушиваемым Сокет также может сделать клиентский сокет, который также может быть конвейером, eventfd, сигналом и т. Д
- Установите прослушивание событий изменения. Вы можете установить тип прослушивания, например, читаемый и доступный для записи. Это читабельно и легко понять. Для прослушивания Сокет-это новое клиентское соединение, которое необходимо принять. Для подключения к клиенту требуется recv. Записываемые события сложнее понять. Сокет имеет кэш. Если вы хотите отправить 2 м данных в клиентское соединение, вы не можете отправить их одновременно. Кэш TCP по умолчанию в операционной системе составляет всего 256 КБ. За один раз можно отправить только 256 тысяч. После того, как кэш заполнится, send вернет ошибку eagain. В это время нам нужно прослушивать события, доступные для записи. В чистом асинхронном программировании мы должны прослушивать события, доступные для записи, чтобы гарантировать, что операция отправки полностью не блокируется.
- Del удаляется из реактора и больше не прослушивает события
- Обратный вызов-это соответствующая логика обработки после наступления события, которая обычно формулируется при добавлении/наборе. Язык C использует указатель на функцию, JS может использовать анонимную функцию, PHP может использовать анонимную функцию, массив методов объектов, имя строковой функции.
Реактор-это всего лишь генератор событий. Фактические операции с дескриптором сокета, такие как подключение/прием, отправка/повторное подключение и закрытие, выполняются в режиме обратного вызова. Конкретный код может ссылаться на следующий псевдокод:
Модель реактора также может использоваться в сочетании с несколькими процессами и несколькими потоками для реализации асинхронного неблокирующего ввода-вывода и многоядерного. В настоящее время популярные асинхронные серверные программы выглядят следующим образом: например
- Nginx: многопроцессный реактор
- Nginx + Lua: многопроцессный реактор + сотрудничество
- Golang: однопоточный реактор + многопоточный процесс
- Свул: многопоточный реактор + многопроцессорный рабочий
В чем заключается этот процесс
С точки зрения базовой технологии этот процесс фактически представляет собой модель асинхронного реактора ввода-вывода. Уровень приложений сам реализует планирование задач и переключает выполняемые в данный момент пользовательские потоки с помощью реактора. Однако существование реактора не воспринимается в пользовательском коде.
Практика программирования параллельного ввода-вывода на PHP
Расширения, связанные с PHP
- Поток: инкапсуляция сокета, предоставляемая ядром PHP
- Сокеты: инкапсуляция базового API сокетов
- Libevent: инкапсуляция библиотеки libevent
- Событие: На основе более продвинутой инкапсуляции libevent он обеспечивает поддержку объектно-ориентированного интерфейса, таймера и обработки сигналов
- Pcntl/POSIX: поддержка управления несколькими процессами, сигналами и процессами
- Pthread: многопоточность, управление потоками, поддержка блокировки
- PHP также имеет расширения для общей памяти, семафоров и очередей сообщений
- PECL: библиотека расширений PHP, включая системный нижний уровень, анализ данных, алгоритм, драйвер, научный калькулятор, графику и т.д. Если она не найдена в стандартной библиотеке PHP, вы можете найти нужную функцию в PECL.
Преимущества и недостатки PHP
Преимущества PHP:
- Первый из них прост. PHP проще, чем любой другой язык. Если вы запустите PHP, вы сможете запустить его через неделю. У C + + есть книга под названием “21 день углубленного изучения C + +”, на самом деле, невозможно выучить за 21 день или даже сказать, что C + + нельзя освоить в глубину без 3-5 лет. Но PHP определенно может быть запущен через семь дней. Таким образом, число PHP-программистов очень велико, и набор персонала проще, чем на других языках.
- PHP очень мощный, потому что официальная стандартная библиотека PHP и библиотека расширений предоставляют 99% возможностей, которые можно использовать для программирования на сервере. Любая функция, которую вы хотите использовать в библиотеке расширений PECL PHP.
Кроме того, PHP имеет историю более 20 лет, и экосистема очень велика. Вы можете найти много кода в GitHub.
Недостатки PHP:
- Производительность низкая, потому что это динамический скрипт, который не подходит для интенсивной работы. Если та же самая PHP-программа написана на C/C + +, версия PHP в 100 раз хуже, чем она.
- Мы все знаем, что спецификация именования функций оставляет желать лучшего. PHP уделяет больше внимания практичности, без каких-либо спецификаций. Названия некоторых функций сбивают с толку, поэтому каждый раз вам приходится обращаться к руководству по PHP.
- Детализация интерфейса структуры данных и предоставляемых функций является относительно грубой. PHP имеет только одну структуру данных массива, а базовый слой основан на хэш-таблице. Массив PHP задает функции карты, набора, вектора, очереди, стека, кучи и других структур данных. Кроме того, в PHP есть SPL, который обеспечивает инкапсуляцию классов других структур данных.
Итак, PHP
- PHP больше подходит для программ практического прикладного уровня, развития бизнеса и быстрой реализации
- PHP не подходит для разработки базового программного обеспечения
- Использование статических языков компиляции, таких как C/C++, Java и golang, в качестве дополнения к PHP, сочетание динамического и статического
- Автоматическое завершение и синтаксическое приглашение с помощью IDEA
Расширение Swool для PHP
Основываясь на вышеуказанных расширениях, вы можете полностью реализовать асинхронные сетевые серверные и клиентские программы с помощью чистого PHP. Но для реализации многопоточности, такой как ввод-вывод, все еще предстоит проделать много утомительной работы по программированию, в том числе по управлению соединением, обеспечению атомарности отправки и приема данных и работе с сетевым протоколом. Кроме того, производительность PHP-кода в части обработки протокола относительно низкая, поэтому я запустил новый проект с открытым исходным кодом swool, который использует язык C и PHP для завершения этой работы. Гибкие бизнес – модели разработаны на PHP с высокой эффективностью. Базовый нижний уровень и часть обработки протокола реализованы на языке Си для обеспечения высокой производительности. Он загружается в PHP расширенным способом, обеспечивая полную сетевую коммуникационную структуру, а затем PHP-код для написания какого-либо бизнеса. Его модель основана на многопоточном реакторе + многопроцессорном работнике, который поддерживает как полную асинхронность, так и полуасинхронность.
Некоторые особенности swool:
- Примите поток, чтобы устранить узкое место в производительности и сигнал тревоги о принятии
- Несколько потоков ввода-вывода для лучшего использования многоядерных
- Он обеспечивает два режима: полный асинхронный и полу-синхронный и полу-асинхронный
- Частичный асинхронный режим для обработки высокоскоростного ввода-вывода
- Синхронный режим для сложной части бизнес-логики
- Нижний уровень поддерживает прохождение всех соединений, отправку данных друг другу, автоматическое объединение и разделение пакетов данных, а также атомарность отправки.
Модель процесса/потока Swole:
Процесс выполнения программы swool:
Использование расширения PHP + swool для реализации асинхронного программирования связи
Код экземпляра можно просмотреть на https://github.com/swoole/swo… Домашняя страница.
TCP – сервер и клиент
Асинхронный TCP-сервер:
Вот, новый свул? Объект сервера передается в параметрах хоста и порта для прослушивания, а затем устанавливаются три функции обратного вызова, а именно: onconnect имеет новое соединение, onReceive получил данные от клиента, и onclose закрыл соединение. Наконец, вызовите команду пуск, чтобы запустить серверную программу. Нижний слой шерсти запустит соответствующее количество потоков реактора и рабочих процессов в соответствии с количеством ядер процессора в текущей машине.
Асинхронный клиент:
Использование клиента аналогично использованию сервера, но существует четыре события обратного вызова. При подключении успешно подключается к серверу, а затем может отправлять данные на сервер. Ошибка Onerror не удалось подключиться к серверу. Сервер onReceive отправил данные в клиентское соединение. Соединение onclose закрыто.
После настройки обратного вызова события инициируйте подключение к серверу. Параметрами являются IP, порт и время ожидания сервера.
Клиент синхронизации:
Клиенту синхронизации не нужно устанавливать какой-либо обратный вызов события. Он не слушает реактор и блокирует последовательный. Дождитесь завершения ввода-вывода, прежде чем переходить к следующему шагу.
Асинхронная задача:
Функция асинхронной задачи используется для выполнения трудоемкой или блокирующей функции в чисто асинхронной серверной программе. Базовая реализация использует пул процессов. После того, как задача будет выполнена, на финише будет запущен, и результат обработки задачи может быть получен в программе. Например, необходимо транслировать мгновенное сообщение. Если он транслируется непосредственно в асинхронном коде, это может повлиять на обработку других событий. Кроме того, асинхронная задача также может использоваться для чтения и записи файлов, поскольку дескриптор файла не может контролироваться с помощью реактора, подобного сокету. Поскольку дескриптор файла всегда доступен для чтения, прямое чтение файла может заблокировать серверную программу, поэтому использование асинхронной задачи является хорошим выбором.
Асинхронный миллисекундный таймер
Эти два интерфейса реализуют функции setinterval и setTimeout, аналогичные JS, которые могут быть настроены для реализации функции с интервалом n мс или выполнения функции после интервала n мс.
Асинхронный клиент MySQL
Swool также предоставляет асинхронный клиент MySQL со встроенным пулом подключений, который может устанавливать максимальное количество подключений с использованием mysql. Одновременные запросы SQL могут повторно использовать эти соединения вместо их повторного создания, что может защитить MySQL от нехватки ресурсов подключения.
Асинхронный клиент redis
Асинхронная веб-программа
Логика программы заключается в считывании данных из redis и отображении HTML-страницы. Свойства AB заключаются в следующем:
Результаты тестирования производительности той же логики в php-fpm следующие:
Программа Websocket
Вульф имеет встроенный сервер websocket, который может реализовывать функцию активного нажатия веб-страницы, такую как веб-обмен мгновенными сообщениями. Существует проект с открытым исходным кодом для справки. https://github.com/matyhtf/ph…