Перевод транскрипции подкаста, подготовленной в преддверии начала курса «Бэкенд-разработчик на PHP»
Описание
В этом выпуске новостей PHP Internals я беседую с Никитой Поповым ( Твиттер , GitHub , Веб-сайт ) о проблемах с предварительной загрузкой PHP 7.4 и его RFC WeakMaps. СКАЧАТЬ MP3Стенограмма
Дерик Ретанс 0:16 Привет, я Дерик.А это новости внутреннего устройства PHP — еженедельный подкаст, посвященный демистификации развития языка PHP. Это 38-я серия.
Я собираюсь обсудить с Никитой Поповым кое-что, что произошло за каникулы.
Никита, как прошли каникулы? Никита Попов 0:34 Мои каникулы были замечательными.
Дерик Ретанс 0:36 Я думал, что начну разговор иначе, чем в прошлом году.
В любом случае, сегодня утром я хочу поговорить с вами о том, что произошло с PHP 7.4 в этот праздничный сезон.
А именно о проблемах с предзагрузкой PHP 7.4 на Windows. Я понятия не имею, в чем на самом деле проблема.
Не могли бы вы объяснить мне это? Никита Попов 0:56 На самом деле, в ранних версиях PHP 7.4 было довольно много проблем с предварительной загрузкой.
Эта функция определенно не была достаточно протестирована.
Большинство проблем были исправлены в 7.4.2. Но если вы используете preload-user (это то, что вам следует использовать, если у вас есть root-доступ), то вы, вероятно, все равно будете испытывать сбои, которые не будут исправлены до следующего выпуска.
Дерик Ретанс 1:20 В 7.4.3. Никита Попов 1:22 Да.
Но вернемся к Windows. Windows имеет совершенно другую архитектуру процессов, чем Linux. В частности, в Linux или BSD есть форк, который просто берет процесс и копирует все его состояние в память, чтобы создать новый процесс.
Это намного дешевле, чем кажется, потому что память повторно используется до тех пор, пока она не изменится.
Дерик Ретанс 1:48 Это копирование при записи.
Никита Попов 1:49 А именно копирование при записи.
Той же функциональности нет в Windows или, по крайней мере, она не широко доступна.
Так что в Windows вы можете создавать новые процессы только с нуля, не переиспользуя память предыдущих.
А для OPcache это проблема, поскольку OPcache хотел бы ссылаться на внутренние классы, определенные PHP. Но поскольку мы храним объекты в общей памяти, которая используется несколькими процессами, у нас возникает проблема: эти внутренние классы могут располагаться по разным адресам в разных процессах.
В Linux у нас всегда будет один и тот же адрес, потому что мы разветвляемся и адрес сохраняется.
В Windows каждый процесс может иметь свой адрес.
И особенно потому, что Windows, насколько я помню, начиная с Windows Vista, использует рандомизацию адресного пространства.
Скорее всего, это почти всегда будет другой адрес.
Дерик Ретанс 2:51 Из соображений безопасности? Никита Попов 2:52 Точно.
По соображениям безопасности.
Дерик Ретанс 2:54 Если вы запустите новый процесс вместо разветвления, станет ли это проблемой для Linux? Никита Попов 2:59 Да, это будет проблемой.
Разница в том, что в Unix мы этого не делаем.
OPcache в Windows имеет совершенно другую архитектуру.
В Linux мы не разрешаем подключаться к существующему OPcache из отдельного процесса.
Таким образом, единственный способ разделить OPcache — использовать форк.
В Windows из-за ограничения отсутствия вилки мы разрешаем такие соединения, и здесь нам приходится сталкиваться с рядом проблем.
Так что на самом деле это общая проблема, а не только предварительная загрузка, с той лишь разницей, что обычно мы можем просто сказать: ну ладно, мы не разрешаем никаких ссылок на внутренние классы из разделяемой памяти в Windows. Это небольшой недостаток с точки зрения оптимизации, но он не очень важен.
А вот что касается предварительной загрузки, то нам приходится привязывать весь граф класса во время его выполнения.
А если у вас есть классы, которые наследуются от внутреннего класса, например от Exception. Или, в некоторых случаях, вы можете просто использовать внутренний класс в качестве подсказки типа, тогда вы не сможете хранить ссылки такого типа в общей памяти в Windows. А поскольку при предварительной загрузке почти неизбежно вы попадаете в подобную ситуацию, оказывается, что вы просто не можете использовать предварительную загрузку в Windows. Дерик Ретанс 4:18 Следовательно, решение состоит в том, чтобы отключить его, а не пытаться заставить его работать, но почти всегда безуспешно.
Никита Попов 4:24 Да, я имею в виду, раньше все работало нормально, просто получалось несколько предупреждений о том, что эти классы не были предварительно загружены.
И люди могут попасть в ситуацию, когда попробуют это на простых примерах и по результатам придут к выводу, что предварительная загрузка работает отлично.
Но как только они добираются до своего действительно сложного приложения, использующего внутренние классы в разных местах, оказывается, что на самом деле нет, на практике это не работает. И поэтому единственный способ избежать проблем – отключить его.
Дерик Ретанс 4:51 На данный момент это кажется самым разумным решением, но думаете ли вы, что в какой-то момент это можно будет исправить другим умным способом? Никита Попов 4:58 Основной способ борьбы с этим — избегать вложенности нескольких процессов в Windows. Альтернативой использованию нескольких процессов является наличие нескольких потоков, которые совместно используют адресное пространство.
По сути то же самое, что и fork, только с потоками.
Но это, конечно, зависит от того, какой веб-сервер и SAPI вы используете.
И я думаю, что в наши дни многопоточные веб-серверы несколько более популярны в Windows, чем в Linux, но это все еще не является основной тенденцией развития.
Дерик Ретанс 5:34 Я думаю, что модели потокового процесса в Windows были гораздо более распространены, когда PHP впервые появился для Windows, потому что это был модуль ISAPI, который всегда был многопоточным.
Насколько я помню, это основная причина, по которой у нас изначально был ZTS. Да, в какой-то момент они начали переходить на модели PHP FPM, потому что они не использовали многопоточность и, следовательно, были намного безопаснее в использовании.
Никита Попов 5:57 Верно.
Я имею в виду, что у потоков возникают проблемы, в частности потому, что такие вещи, как локали, относятся к процессу, а не к потоку.
Таким образом, процессы, как правило, безопаснее в использовании.
Дерик Ретанс 6:08 Произошло ли еще что-нибудь интересное, что пошло не так с предварительной загрузкой, или вы не хотите об этом говорить? Никита Попов 6:12 Остальное связано главным образом с тем, что у нас есть два разных способа реализации предварительной загрузки.
Один использует файл компиляции OPcache, а другой использует require или include, и разница между ними заключается в том, что файл компиляции OPcache объединяет файл, но не выполняется.
В этом случае способ предварительной загрузки заключается в том, что мы сначала собираем все классы, а затем постепенно связываем их, фактически регистрируем их, всегда проверяя, что все зависимости уже связаны.
И я думаю, что этот способ в целом хорошо работал с момента выпуска PHP 7.4. И еще один подход require, при котором фактически require непосредственно выполняет код и регистрирует классы.
И в этом случае, если окажется, что какая-то зависимость по какой-то причине не может быть предварительно загружена, нам просто придется прервать предзагрузку, потому что мы не сможем после этого восстановиться.
Это прерывание отсутствовало.
И как оказалось, в конце концов, люди на практике используют предварительную загрузку, используя подход require, а не подход файла компиляции OPcache. Дерик Ретанс 7:26 Хотя это пример, который можно увидеть в большинстве примеров, которые я видел, и в документации.
Никита Попов 7:30 Да, у него есть некоторые преимущества перед require. Дерик Ретанс 7:34 Что еще произошло за каникулы, так это то, что вы работали над несколькими RFC, о которых было бы слишком долго рассказывать в этом эпизоде.
Но одним из первых был WeakMap или WeakMaps RFC, который был построен на основе слабых звеньев, которые уже есть в PHP 7.4. Что не так со слабыми ссылками и зачем нам нужны слабые ассоциативные массивы? Никита Попов 7:58 Слабые звенья — это нормально.
Напомню, что такое слабые ссылки — они позволяют ссылаться на объект, не исключая его из цикла сборки мусора.
Поэтому, если объект будет уничтожен, у вас останется висящая ссылка.
Если вы попытаетесь получить к нему доступ, вы получите некоторую информацию об объекте.
Безусловно, наиболее распространенным вариантом использования любой слабой структуры данных является ассоциативный массив (карта), в котором у вас есть объекты и вы хотите связать с ними некоторые данные.
Типичными вариантами использования являются кэши и другие структуры данных с запоминанием.
И причина, по которой важно, чтобы эта структура была слабой, заключается в том, что вы бы не хотели.
хммм, скажем, если вы хотите кэшировать некоторые данные, связанные с объектом, но никто больше не будет использовать этот объект, вы бы это сделали.
Я не хочу хранить эти данные в кеше, потому что никто никогда не будет использовать их снова.
Они будут лишь бессмысленно занимать память.
Здесь в игру вступает WeakMap. Здесь вы используете объекты в качестве ключей и некоторые данные в качестве значений.
И если объект больше не используется вне этого ассоциативного массива, он также удаляется из него.
Дерик Ретанс 9:16 Итак, вы упомянули объекты как ключи.
Это что-то новое? Потому что я не думаю, что PHP в настоящее время поддерживает это.
Никита Попов 9:22 Да, вы не можете использовать объекты в качестве ключей в обычных массивах.
Это не сработает. Но, например, интерфейс ArrayAccess и интерфейс Traversable не заботятся о том, какие у вас типы.
Таким образом, вы можете использовать что угодно в качестве ключей.
Дерик Ретанс 9:37 Я освежил свою память на этот счет, да.
Но слабая карта — это то, что затем реализует ArrayAccess. Никита Попов 9:44 Верно.
Дерик Ретанс 9:45 Как выглядит интерфейс Weak Map? Как бы вы с ним взаимодействовали? Никита Попов 9:49 Ну, на самом деле, он просто реализует все волшебные интерфейсы PHP. Итак, ArrayAccess — вы можете получить доступ к слабой карте по ключу, где ключ — это объект. Traversable — то есть вы можете перебирать слабую карту и получать ключи и значения, и, конечно же, Countable, чтобы вы могли посчитать, сколько там элементов.
Вот и все.
Дерик Ретанс 10:12 Все эти методы, их много, их должно быть девять или десять или что-то в этом роде, верно? Никита Попов 10:17 Пять.
Дерик Ретанс 10:18 Нет, есть еще шесть итераторов.
Никита Попов 10:20 Да, да, есть небольшая деталь: при реализации внутренних классов Traversable вам на самом деле не нужно реализовывать методы итератора.
Поэтому их там немного меньше.
Дерик Ретанс 10:33 Кому будет полезна эта новая функция? Никита Попов 10:35 Один из пользователей слабых карт, вроде ORM. Где записи базы данных представлены в виде объектов, и с этими объектами связано хранилище данных.
И я думаю, что это хорошо известная проблема: если вы используете ORM, вы иногда можете столкнуться с проблемами использования памяти.
И отсутствие слабых структур – одна из причин, почему такое может произойти.
Потому что они просто продолжают хранить информацию, даже если приложение больше ее не использует. Дерик Ретанс 11:12 Запрашивал ли какой-либо конкретный ORM эту функцию? Никита Попов 11:15 Я так не думаю.
Дерик Ретанс 11:16 Поскольку слабые карты являются своего рода внутренним классом PHP, как эти вещи реализуются? Есть что-нибудь интересное? Потому что я помню, как в прошлом году говорил с Джо о слабых ссылках — есть некоторая функциональность, которая автоматически что-то делает с деструктором, а точнее с объектами.
Это то же самое происходит и со слабыми картами? Никита Попов 11:37 Итак, механизм работы слабых звеньев и ассоциативных массивов практически одинаков.
Таким образом, у каждого объекта есть флаг, который можно установить, чтобы указать, что на него ссылается слабая ссылка или слабый ассоциативный массив.
Если объект уничтожен и имеет этот прекрасный флаг, мы выполняем обратный вызов, который удалит объект из Weak Reference или Weak Map, или из нескольких ассоциативных массивов одновременно.
Дерик Ретанс 12:05 Это потому, что существует какой-то реестр, который привязывает объект? Никита Попов 12:08 Поскольку мы храним объекты как часть слабых ссылок и слабых ассоциативных массивов, мы можем эффективно удалить их.
Дерик Ретанс 12:16 Когда я читал RFC, я увидел упоминание о чем-то вроде идентификатора объекта SPL, который позволяет идентифицировать конкретный объект. Это из-за слабых ссылок или слабых ассоциативных массивов? Либо это что-то, что больше не используется, либо людям больше не следует его часто использовать, потому что, я думаю, раньше это был способ идентифицировать объект и затем связать с ним больше данных.
Как вы упомянули, что ORM должны были делать для кеширования.
Никита Попов 12:44 Верно.
Это как-то связано и в то же время нет. Одно не заменяет другое, это просто разные варианты использования.
Раньше у нас очень долгое время был SPL-хеш объекта.
И я думаю, что где-то в PHP 7.0 или, может быть, позже, был введен идентификатор объекта SPL, который является целым числом и, следовательно, более эффективен.
Но в конечном итоге эти функции возвращают уникальный идентификатор объекта.
Но этот идентификатор уникален только до тех пор, пока объект жив.
Эти идентификаторы объектов используются повторно при уничтожении объектов.
Дерик Ретанс 13:30 И делает ли это их непригодными для связывания данных кэша с конкретным объектом? Никита Попов 13:35 Это делает их подходящими для привязки данных кэша.
Но вам также следует сохранить объект, чтобы убедиться, что он не будет уничтожен.
Это позволяет обойти ограничение, заключающееся в том, что вы не можете использовать объекты в качестве ключей массива.
Вот для чего вам нужен идентификатор.
Но вам все равно следует хранить строгую ссылку на объект, чтобы гарантировать, что он не является мусором.
Ведь этот идентификатор начинает ссылаться на какие-то другие объекты.
Дерик Ретанс 14:04 Когда вы говорите «Сильная ссылка», это то, что традиционно называют ссылками в PHP? Никита Попов 14:08 Это обычная ссылка.
Дерик Ретанс 14:10 Ну, потому что с момента подачи RFC до его принятия прошло довольно много времени, я так понимаю? Никита Попов 14:16 Принимается: 25, ноль Дерик Ретанс 14:18 25, ноль.
Это случается не очень часто.
Никита Попов 14:22 Большинство RFC не могут быть анонимными, но обычно они либо принимаются, либо категорически отвергаются.
Промежуточных решений не так много.
Дерик Ретанс 14:34 Это очень хорошо.
В любом случае, мы увидим это в PHP 8, я думаю, в конце года.
Никита Попов 14:39 Это верно.
Да.
Дерик Ретанс 14:41 Что ж, спасибо, что нашли время поговорить со мной о слабых ссылках и предварительной выборке, особенно в Windows. Спасибо, что нашли время.
Никита Попов 14:50 Спасибо, что пригласил меня, Дерик.
Дерик Ретанс 14:52 Спасибо, что прослушали этот выпуск новостей внутреннего устройства PHP — еженедельного подкаста, посвященного демистификации эволюции языка PHP. Я веду учетную запись Patreon для поклонников этого подкаста, а также инструмента отладки Xdebug. Вы можете подписаться на Patreon по адресу .
Если у вас есть комментарии или предложения, не стесняйтесь присылать их по электронной почте.
Спасибо, что выслушали, увидимся на следующей неделе.
Заметки подкаста
РФЦ: Слабые картыПодробнее о курсе «Бэкенд-разработчик на PHP»
Теги: #backend #php #otus #предварительная загрузка #WeakMaps
-
Устранить Все Ошибки Реестра
19 Oct, 24 -
Цитология
19 Oct, 24 -
Спамеры Манипулируют Ценами На Акции
19 Oct, 24 -
10 Причин Ненавидеть Веб 2.0
19 Oct, 24