![Badoo перешли на PHP7 и сэкономили $1M](https://habrastorage.org/files/c7b/553/eb3/c7b553eb351846a3adaac2bf6883bbee.jpg)
Мы сделали это! Несколько сотен наших application-серверов переведены на PHP7 и прекрасно себя чувствуют. Насколько нам известно, это второй переход на PHP7 проекта такого масштаба (после Etsy). В процессе мы нашли несколько очень неприятных багов в системе кеширования байт-кода PHP7, но они исправлены. А теперь — ура! — благая весть для всего PHP-сообщества: PHP7 действительно готов к продакшену, стабилен, потребляет значительно меньше памяти и дает очень хороший прирост производительности. Ниже мы подробно расскажем, как мы перешли на PHP7, с какими трудностями столкнулись, как с ними боролись и какие результаты получили. Но начнем с небольшого введения.
Мнение о том, что узким местом в веб-проектах является база данных — одно из самых распространенных заблуждений. Хорошо спроектированная система сбалансирована — при увеличении входной нагрузки удар держат все части системы, а при превышении пороговых значений тормозить начинает все: и процессор, и сетевая часть, а не только диски на базах. В этой реальности процессорная мощность application-кластера является чуть ли не самой важной характеристикой. Во многих проектах этот кластер состоит из сотен или даже тысяч серверов, поэтому «тюнинг» процессорной нагрузки на кластере приложений оказывается более чем оправданным экономически (миллион долларов в нашем случае).
Процессор в веб-приложениях PHP «съедает» столько же, сколько и любой высокоуровневый динамический язык — много. Но у PHP-разработчиков годами была особенная печаль (и повод для сильнейшего «троллинга» со стороны прочих сообществ) — отсутствие в PHP «честного» JIT или хотя бы генератора в компилируемый текст на языках типа С и С++. Неспособность сообщества предоставить подобные решения в рамках основного проекта породило неприятную тенденцию: крупные игроки стали придумывать собственные решения. Так появились HHVM в Facebook, KPHP во «Вконтакте», наверняка были и другие «поделки».
К счастью, в 2015 году сделан первый шаг к тому, чтобы PHP стал «взрослее»: вышел PHP7. JIT в PHP7 так и не появился, однако результат изменений в «движке» трудно переоценить: теперь на многих задачах PHP7 даже без JIT не уступает HHVM (см., например, бенчмарки из блога LiteSpeed и бенчмарки из презентации разработчиков PHP7). Новая архитектура PHP7 также упрощает дальнейшее добавление JIT.
Платформенные разработчики в Badoo пристально следили за этими страстями последние несколько лет и даже сделали пилотный проект с HHVM, но решили дождаться PHP7, поскольку посчитали его более перспективным. И недавно мы запустили Badoo на PHP7! Это был эпичный проект как минимум из-за размера: у нас больше 3 миллионов строк кода на PHP и 60 000 тестов. О том, как мы со всем этим справились, попутно придумав новый фреймворк для тестирования PHP-приложений (уже выпустили в open source — похож на Go! AOP) и сэкономили целый миллион — читайте дальше.
Опыты с HHVM
Перед переходом на PHP7 мы некоторое время искали другие способы оптимизировать наш backend. Конечно, первым делом мы решили «поиграться» с HHVM.
Потратив пару недель на исследование, мы получили весьма достойные результаты: после прогрева JIT на нашем фреймворке выигрыш по скорости и использованию CPU составлял сотни процентов.
Однако HHVM обладал неприятными недостатками:
- сложный и медленный деплой. При деплое необходимо обязательно прогревать JIT-кеш. В момент прогрева машина не должна быть нагружена продакшен-трафиком, потому как работает все достаточно медленно. Прогревать параллельными запросами тоже не рекомендуется. Короче, фаза прогрева большого кластера — операция небыстрая, плюс на большой кластер в несколько сотен машин надо научиться выкладывать порционно. В итоге получаем нетривиальную архитектуру и процедуру деплоя с непредсказуемым временем работы. А мы хотим иметь максимально простой и быстрый деплой: важной частью нашей девелоперской культуры является выкладка двух плановых релизов в день и возможность быстро «раскатать в бой» хотфиксы;
- неудобство тестирования. Для юнит-тестирования мы активно использовали расширение runkit, которое отсутствует в HHVM. Мы подробнее расскажем об этом дальше, но если вы вдруг не в курсе, то это такое расширение, которое позволяет на лету менять поведение переменных, классов, методов, функций практически как угодно, и делается это через весьма «хардкорную» интеграцию с «внутренностями» PHP. Ядро HHVM лишь отдаленно похоже на ядро PHP, так что эти самые «внутренности» там абсолютно разные. Поэтому реализовать runkit поверх HHVM самостоятельно — адов труд: из-за особенностей расширения нам пришлось бы переписывать десятки тысяч тестов, чтобы убедиться, что HHVM правильно работает с нашим кодом. Нам это показалось нецелесообразным. Если быть честными, это было проблемой любого из вариантов, и при переходе на PHP7 нам все равно пришлось переделать очень многое, в том числе выкинуть runkit, но об этом позже;
- совместимость. В первую очередь это неполная совместимость с PHP 5.5 (см. github.com/facebook/hhvm/blob/master/hphp/doc/inconsistencies, github.com/facebook/hhvm/issues?labels=php5+incompatibility&state=open) и несовместимость с уже написанными расширениями, а у нас их десятки. Обе несовместимости вытекают из очевидного структурного недостатка проекта: HHVM разрабатывается не сообществом, а отделом внутри Facebook. В таких случаях компаниям значительно проще поменять внутренние правила и стандарты, не оглядываясь на сообщество и тонны уже написанного кода. Им проще переделать все под себя, решить проблему своими ресурсами. Поэтому, чтобы успешно работать при таких же объемах задач, нужно иметь сравнимый по мощности ресурс и для первичного этапа внедрения, и для дальнейшей поддержки. Это рискованно и потенциально дорого — мы так рисковать не хотели;
- перспективы. Несмотря на то, что Facebook — большая компания с классными программистами, у нас были большие сомнения в том, что отдел разработки HHVM может оказаться мощнее PHP-сообщества. Мы полагали, что как только внутри PHP появится что-то похожее, все доморощенные проекты начнут медленно, но верно умирать.
И мы стали ждать PHP7.
Переход на новую версию интерпретатора — важный и сложный процесс, поэтому мы готовились к нему, составив четкий план перехода. Он состоял из трех этапов подготовки:
- изменение инфраструктуры для сборки и деплоя PHP и адаптация множества написанных нами расширений;
- изменение инфраструктуры и окружения тестирования;
- изменения PHP-кода приложений.
Расскажем обо всех этапах подробнее.
Исправления в ядре и расширениях
У нас есть собственная, активно поддерживаемая и дорабатываемая ветка PHP. Мы начали проект по переводу Badoo на PHP7 еще до его официального релиза, поэтому нам надо было плавно обеспечить регулярный rebase PHP7 upstream в наше дерево, чтобы иметь возможность получать обновления каждого релиз-кандидата. Все патчи и кастомизации (см. секцию «Патчи» нашего техсайта tech.badoo.com/open-source), которые мы используем в повседневной работе, также должны были быть портируемыми между версиями и работать корректно.
Мы автоматизировали выкачивание и сборку всех зависимостей, экстеншенов и дерева PHP под 5.5 и 7.0. Это не только упростило работу, но и дало хороший задел на будущее: когда выйдет версия 7.1, у нас уже все будет готово.
Над экстеншенами тоже пришлось попотеть. Мы подерживаем около 40 расширений, причем больше половины — внешние расширения open source с нашими доработками.
Для максимально быстрого перехода мы решили запустить параллельно два процесса. Первый — перепиcать самостоятельно самые критичные для нас расширения: шаблонизатор Blitz, кеш данных APcu в Shared memory, сбор статистики в Pinba и некоторые кастомные для работы с внутренними сервисами (в итоге около 20 расширений).
Второй — активно избавляться от расширений, которые используются в некритичных частях инфраструктуры. Легко избавиться нам удалось от 11 расширений — немало!
И, конечно, мы начали активно общаться с людьми, которые поддерживают основные открытые расширения, используемые нами, на предмет совместимости с PHP7 (отдельное спасибо Дерику Ретансу (англ. Derick Rethans), который разрабатывает Xdebug).
Далее мы немного подробнее остановимся на технических деталях портирования расширений под PHP7.
В 7-й версии PHP-разработчики изменили много внутренних API, что вызвало необходимость правки большого количества кода в экстеншенах. Самые важные изменения следующие:
- zval * → zval. Ранее, при создании новой переменной, структура zval всегда аллоцировалась, а теперь используется структура из стека;
- char * → zend_string. В PHP7 используется агрессивное кеширование строк в ядре PHP, поэтому в новом ядре повсеместно перешли с обычных строк на структуру zend_string, в которой хранится строка и ее длина;
- изменения в API массивов. Теперь используется zend_string в качестве ключа, в имплементации массивов заменили double linked list на обычный массив, который выделяется одним блоком вместо множества маленьких.
Все это позволило кардинально уменьшить количество небольших аллокаций памяти и в результате ускорить ядро PHP на десятки процентов.
Нужно отметить, что все эти изменения повлекли за собой необходимость если не переписывать, то активно править все экстеншены. Если в случае со встроенными экстеншенами мы могли рассчитывать на их авторов, то наши разработки могли править только мы, а правок нужно было много: из-за изменения внутренних API некоторые части кода было проще переписать.
К сожалению, введение новых структур, использующих сбор мусора, одновременно с ускорением выполнения кода усложняет сам движок и нахождение проблем в нем. Одной из них стала проблема в OPcache, которая заключалась в следующем: при очистке кеша байт-код закешированного файла разрушался в тот момент, когда он еще мог использоваться в другом процессе, что приводило к падению процесса. Внешне это выглядело так: строки (zend_string) в названиях функций или констант вдруг разрушаются и вместо них появляется мусор.
Поскольку у нас используется значительное количество экстеншенов собственной разработки, многие из которых активно работают со строками, то в первую очередь подозрение пало на неправильное использование строк в них. Написали много тестов, провели много экспериментов, но всё безрезультатно. В итоге пришлось обратиться за помощью к основному разработчику ядра PHP — Дмитрию Стогову.
В первую очередь он спросил, очищался ли кеш. Выяснили, что, действительно, в каждом случае так и было. Стало понятно, что проблема все-таки не у нас, а в OPcache. Мы быстро воспроизвели проблему и Дмитрий исправил ее в течение пары дней. Без этого исправления, которое вошло в версию PHP 7.0.4, использовать ее стабильно в продакшене было нельзя!
Изменение инфраструктуры тестирования
Тестирование в Badoo — наша особенная гордость. PHP-код мы выкладываем в продакшен 2 раза в день, в каждую выкладку у нас попадает 20-50 задач (мы используем feature branch в Git и автоматизированную сборку билдов с тесной JIRA-интеграцией). При таком графике и объеме задач без автотестов никак.
На сегодняшний день у нас около 60 тысяч юнит-тестов примерно с 50%-м покрытием, которые проходят в среднем за 2-3 минуты в облаке (об этом мы уже рассказывали на Хабре). Помимо юнит-тестов, мы используем автотесты более высокого уровня — интеграционные и системные тесты, selenium-тесты для веб-страниц и calabash-тесты для мобильных приложений. Все это разнообразие позволяет нам в кратчайшие сроки сделать вывод о качестве каждой конкретной версии кода и принять соответствующие решения.
Переход на новую версию интерпретатора — кардинальное изменение. Возможных проблем может быть сколько угодно, поэтому крайне важно, чтобы все тесты работали. Для того чтобы стало понятно, что, как и почему мы делали, необходимо совершить небольшой экскурс в историю и рассказать про эволюцию развития тестов в нашей компании.
Часто люди, задумывающиеся о тестировании своих продуктов, сталкиваются в процессе экспериментов (а некоторые уже при внедрении) с тем, что их код к этому не готов. Действительно, разработчик должен помнить о том, что его код должен быть тестируемым. Архитектура должна позволять юнит-тестам подменять вызовы и объекты внешних зависимостей, чтобы изолировать тестируемый код от внешних условий. Надо сказать, что требование это усложняет жизнь, и многие программисты из принципа не хотят писать код так, чтобы его можно было тестировать — навязываемые ограничения вступают в неравную борьбу с прочими ценностями «хорошего кода» и обычно проигрывают. И часто, представив себе объем имеющегося кода, написанного не по правилам, экспериментаторы просто откладывают тестирование до лучших времен либо пытаются довольствоваться малым, покрывая тестами только то, что можно покрыть (в итоге тесты не всегда дают ожидаемый результат).
Наша компания — не исключение. Мы тоже начали внедрять тестирование далеко не сразу после старта проекта. Было уже написано немало строк кода, который вполне себе работал в продакшене и приносил хорошие деньги. Переписывать весь этот код ради возможности покрыть его тестами так, как рекомендуется, вышло бы слишком долго и дорого.
К счастью, на тот момент уже был отличный инструмент, который позволял решить большинство проблем с нетестируемым кодом — runkit. Это расширение для PHP, которое позволяет во время исполнения скрипта менять, удалять, добавлять методы, классы и функции, используемые в программе. Он может еще много чего, но другие функции расширения мы не использовали. Инструмент разрабатывался и поддерживался в течении нескольких лет (с 2005 по 2008 годы) Сарой Гоулман (англ. Sara Golemon), которая сейчас работает в Facebook, в том числе и над HHVM. А с 2008 года и по сегодняшний день проект поддерживает наш соотечественник Дмитрий Зенович (работал руководителем отделов тестирования в «Бегуне» и Mail.Ru). И мы тоже немножко «наконтрибутили» в проект.
Сам по себе runkit — очень опасный экстеншен. С его помощью можно менять константы, функции и классы прямо во время работы скрипта, который их использует. По сути, это инструмент, с помощью которого можно перестроить ваш самолет прямо во время полета. Runkit лезет в самые внутренности PHP на лету; одна ошибка или недоработка в runkit — и самолет красиво взрывается в воздухе, PHP падает, либо вы проводите много часов в поиске утечек памяти и прочей низкоуровневой отладке. Тем не менее, это был для нас необходимый инструмент: внедрить тестирование в проект без серьезного переписывания можно только так, через изменение кода на лету, просто заменяя его на нужный.
При переходе на PHP7 runkit оказался большой проблемой — эту версию PHP он не поддерживал. Был вариант спонсирования разработки новой версии, но этот путь не казался нам самым надежным в долгосрочной перспективе. Параллельно мы рассматривали несколько других вариантов.
Одним из перспективных решений было перейти с runkit на uopz. Это тоже расширение PHP c похожей функциональностью, появившееся в апреле 2014 года. Его нам предложили коллеги из «Mамбы», дав очень хорошие отзывы в первую очередь о скорости работы. Проект поддерживает Джо Уоткинс (англ. Joe Watkins) из First Beat Media (UK). Выглядел этот проект более живым и перспективным по сравнению с runkit. Но, к сожалению, перевести на uopz все тесты у нас не получилось. Где-то случались фатальные ошибки, где-то сегфолты — мы завели несколько репортов, но по ним, увы, движения нет (подробнее см. например этот баг на github). Обойтись переписыванием тестов в этом случае получилось бы очень дорого, да и не факт, что не выявилось бы что-то еще.
В результате мы пришли к очевидному для нас решению: раз нам и так необходимо переписать множество кода и при этом все равно зависеть от внешних проектов типа runkit или uopz, с которыми у нас постоянно появляются проблемы, которые очень дорого либо невозможно решать самостоятельно, то почему бы уже не переписать код так, чтобы по максимуму все зависимости убрать? Да так, чтобы подобных проблем у нас больше никогда не возникало, даже если мы захотим перейти на HHVM или любой другой подобный продукт. И тогда у нас появился свой фреймворк.
Система получила название SoftMocks. Слово soft подчеркивает, что система работает на чистом PHP вместо использования расширений. Это проект open source, он доступен в виде подключаемой библиотеки и находится в открытом доступе. SoftMocks не завязан на особенности реализации ядра PHP и работает с помощью переписывания кода на лету, по аналогии с фреймворком Go! AOP.
В нашем коде тестов в основном используются следующие вещи:
- Подмена реализации одного из методов класса.
- Подмена результата выполнения функции.
- Изменение значения глобальной константы или константы класса.
- Добавление метода в класс.
Все эти возможности прекрасно реализуются с помощью runkit. При переписывании кода это становится возможным, но с некоторыми оговорками.
Описание работы SoftMocks — материал для отдельной статьи, которую мы в ближайшее время напишем. А пока ограничимся лишь кратким описанием работы этой системы:
- пользовательский код подключается через функцию-обертку rewrite. После этого все операторы include автоматически рекурсивно подменяются на обертки;
- внутрь определения каждого пользовательского метода добавляется проверка на существование подмены, и если она есть, то выполняется соответствующий код. Прямые вызовы функций заменяются на вызов через обертку — это позволяет перехватывать как встроенные, так и пользовательские функции;
- обращения к константам в коде также динамически подменяются на вызов обертки;
- SoftMocks в работе использует PHP-Parser Никиты Попова. Эта библиотека не очень быстрая (парсинг примерно в 15 раз медленнее token_get_all), но предоставляет удобный интерфейс для обхода синтаксического дерева и дает удобный API для работы с синтаксическими конструкциями произвольной сложности.
Вернемся к нашей задаче — переходу на PHP7. После того как мы стали использовать в проекте SoftMocks, у нас осталось около 1000 тестов, которые требовалось «починить» вручную. Это можно считать неплохим результатом, если учесть, что изначально у нас было 60 000 тестов. Скорость их прогона по сравнению с runkit не уменьшилась, так что в плане производительности серьезных потерь от использования SoftMocks нет. Справедливости ради отметим, что uopz все-таки должен работать заметно быстрее.
Утилиты и код приложения
Помимо множества нововведений, PHP7 принес с собой и некоторые обратные несовместимости. Первое, с чего мы начали изучение проблемы — это чтение официального migration guide. Быстро стало понятно, что без исправления имеющегося кода мы рискуем как получить в продакшене фатальные ошибки, так и столкнуться с изменением поведения, которое не будет отражено в логах, но приведет к неправильной логике работы приложения.
Badoo — это несколько репозиториев кода на PHP, самый крупный из которых содержит более 2 миллионов строк кода. Причем на PHP у нас реализовано множество вещей: начиная от бизнес-логики веба и бекенда мобильных приложений и заканчивая утилитами тестирования и выкладки кода. Кроме этого ситуация осложнялась тем, что Badoo — проект с историей, ему уже 10 лет, и наследие PHP4, к сожалению, все еще присутствовало. Соотвественно, метод «пристального вглядывания» неприменим. Неприменима и «бразильская система», то есть выложить в продакшен как есть и смотреть, что сломается, чересчур увеличивает риски сломать бизнес-логику для слишком большого процента пользователей. Поэтому мы стали искать возможность автоматизировать поиск несовместимых мест.
Сначала мы попытались использовать наиболее популярные среди разработчиков IDE, но, к сожалению, на тот момент они либо просто не поддерживали синтаксис и особенности PHP7, либо обнаруживали подозрительно мало проблем, пропуская, очевидно, опасные места в коде. После небольшого исследования было решено попробовать утилиту php7mar. Это такой несложный статический анализатор кода, реализованный на PHP. Очень прост в использовании, работает довольно быстро, результат предоставляет в виде текстового файла, требует наличия PHP7. Конечно, данная утилита не является панацеей, имеются как ложные срабатывания, так и пропуски особенно «хитрых» мест в коде. Но около 90% проблем с ее помощью удалось обнаружить, что существенно ускорило и облегчило процесс подготовки кода к работе под PHP7.
Наиболее часто встречающимися и потенциально опасными проблемами для нас были:
- изменение поведения функций func_get_arg() и func_get_args(). В пятой версии PHP эти фунции возвращали значения аргументов функций на момент их передачи, а в седьмой версии — на момент вызова func_get_args(). Таким образом, если внутри функции до вызова func_get_args() переменная агрумента изменяется, то есть риск получить поведение, отличное от пятой версии. Это тот самый случай, когда в логах будет пусто, а бизнес-логика приложения может оказаться сломанной;
- непрямое обращение к переменным, свойствам и методам объектов. И снова опасность в том, что поведение может измениться «молча». В документации достаточно подробно описано, в чем именно заключаются отличия;
- использование зарезервированных имен классов. В PHP7 стало нельзя использовать bool, int, float, string, null, true и false в качестве имени класса. Да-да, у нас был класс Null. К счастью, этот случай уже проще, потому как приводит к ошибке;
- очень много было найдено потенциально проблемных конструкций foreach, которые используют ссылку. Но практически все из них вели себя одинаково в пятой и седьмой версии, так как мы и раньше старались внутри foreach не изменять итерируемый массив и не рассчитывать на его внутренний указатель.
Остальные случаи несовместимости либо встречались крайне редко (как, например модификатор ‘e’ для регулярных выражений), либо исправлялись простой заменой (например, теперь все конструкторы должны называться __construct(), использовать имя класса запрещено).
Но, перед тем как начать исправление кода, мы подумали, что пока одни разработчики вносят необходимые для совместимости изменения, другие будут продолжать писать несовместимый с PHP7 код. Для решения этой проблемы мы добавили pre-receive hook в каждый Git-репозиторий, который выполнял на изменяемых файлах php7 -l, т.е. проверял их на соответствие синтаксису PHP7. Это не гарантирует полную защиту от несовместимоcти, но уже устраняет ряд проблем. В остальных же случаях разработчикам просто приходилось быть чуть внимательнее. Кроме того, мы стали делать регулярный прогон полного набора тестов под PHP7 и сравнивать результаты с прогонами под PHP5. При этом использовать любые новые возможности PHP7 разработчикам было запрещено, т.е. старый pre-receive hook с php5 -l мы не выключали. Это позволило нам в определенный момент получить код, совместимый как с седьмой, так и с пятой версией интерпретатора. Почему это важно? Потому что помимо проблем с PHP-кодом, при обновлении на новую версию возможны проблемы с самим PHP7 и его расширениями (собственно, как сказано выше, мы с этими проблемами и столкнулись). И, к сожалению, не все из них воспроизводились в тестовом окружении, некоторые мы смогли увидеть только под значительной нагрузкой в продакшене.
«Запуск в бой» и результаты
Очевидно, нам требовался простой и быстрый способ менять версию PHP на любом количестве любых серверов. Для этого во всем коде пути к CLI-интерпретатору были заменены на /local/php, который, в свою очередь, являлся симлинком либо на /local/php5, либо на /local/php7. Таким образом, для изменения версии PHP на сервере требовалось изменить ссылку (операция атомарна — это важно для CLI-скриптов), остановить php5-fpm и запустить php7-fpm. Можно было бы иметь в nginx два upstream для php-fpm, запускать php5-fpm и php7-fpm на разных портах, но этот вариант нам не понравился усложением конфигурации nginx.
После того как все вышеперечисленное было выполнено, мы смогли перейти к прогону selenium-тестов в препродакшен-окружении, что позволило нам обнаружить ряд проблем, не замеченных ранее. Они каcались как PHP-кода (например, пришлось отказаться от устаревшей глобальной переменной $HTTP_RAW_POST_DATA в пользу file_get_contents(«php://input»)), так и расширений (разного рода ошибки сегментирования).
Исправив обнаруженные на предыдущем этапе проблемы и закончив переписывание юнит-тестов (в ходе которого нам тоже удалось обнаружить несколько багов в интерпретаторе, например, такой), мы наконец приступили к «карантину» в продакшене. «Карантином» мы называем запуск новой версии PHP на ограниченном числе серверов. Начали с одного сервера в каждом крупном кластере (бекенд веба и мобильных приложений, облако), постепенно увеличивая количество, если ошибок не возникает. Первым крупным кластером, полностью перешедшим на PHP7, стало облако. Причиной этому послужило отсутствие на нем потребности в php-fpm. Тем же кластерам, где работает fpm, пришлось подождать до тех пор, пока мы не обнаружили, а Дмитрий Стогов не исправил проблему с OPcache. После этого мы уже перевели и fpm-кластер.
Теперь о результатах. Если коротко, то они более чем впечатляют. Ниже приведены графики времени ответа, rusage, потребления памяти и использования процессора в самом крупном (263 сервера) из имеющих у нас кластеров, а именно — бекенда мобильных приложений в пражском дата-центре:
Распределение времён ответа:
![](https://habrastorage.org/files/ea3/7bc/878/ea37bc8786374c668900c411a17f1e44.png)
RUsage (CPU time):
![](https://habrastorage.org/files/355/d13/63f/355d1363fc564584b38c4abc129229cc.png)
Memory usage:
![](https://habrastorage.org/files/f91/da0/1c8/f91da01c8f1e414daafce0f1641d8f68.png)
CPU load (%) на всём кластере:
![](https://habrastorage.org/files/4cc/6d1/67a/4cc6d167accc483c9a5657cfe816d729.png)
Таким образом, процессорное время сократилось в 2 раза, что улучшило общее время ответа примерно на 40%, так как некоторая часть времени при обработке запроса тратится на общение с базами и демонами, и с переходом на PHP7 эта часть никак не ускоряется, что ожидаемо. Кроме того, эффект несколько усиливается тем, что общая нагрузка на кластер упала ниже 50%, что указывает на некоторые особенности в работе технологии Hyper-Threading. Грубо говоря, при увеличении нагрузки выше 50% начинают работать HT-ядра, которые не настолько «полезны», как ядра физические, но это уже тема для другой статьи.
Потребление памяти, хотя никогда и не являлось для нас узким местом, снизилось примерно в 8 раз! И, наконец, мы сэкономили на оборудовании — теперь мы можем на том же количестве серверов выдерживать намного большую нагрузку, что, по сути, снижает затраты на его приобретение и обслуживание. Результаты на остальных кластерах отличаются незначительно, разве что выигрыш на облаке чуть скромнее (порядка 40% CPU) из-за отсутствия там OPcache.
Сколько мы сэкономили в деньгах? Давайте посчитаем. Кластер серверов приложений у нас состоит из 600 с лишним серверов. Снизив использование CPU в два раза, мы получаем экономию примерно в 300 серверов. Добавив начальную цену такого «железа» (порядка 4000$ за каждый) и амортизацию, получаем около миллиона долларов экономии плюс около ста тысяч в год на хостинге! И это не считая облака, производительность которого также выросла. Считаем, что это — отличный результат!
А вы уже перешли на PHP7? Будем рады услышать ваше мнение и вопросы в комментариях.