Статьи

Горизонтальное масштабирование PHP-приложений, часть 1

Вы создали сайт. Это было весело, и приятно видеть, как вливаются все эти посетители. Трафик медленно увеличивается, пока однажды кто-то не отправит ссылку на ваше приложение в Reddit и Hacker News, планеты не выровняются, GitHub не работает или что-то в этом роде. По какой-то причине люди замечают почту и штурм, преодолевая все барьеры разума и логики.

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

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

оптимизация

Мы уже писали об этом раньше , и на следующей неделе выйдет более продвинутая статья, но при этом применяется обычный совет: обновитесь до последней версии PHP (в настоящее время 5.5, имеет встроенный OpCache), проиндексируйте вашу базу данных, кэшируйте свою статическую информацию. контент (редко изменяемые страницы, такие как About, FAQ и т. п.) и т. д.

Одним конкретным аспектом оптимизации, который может быть выполнен, является не только кэширование статических ресурсов, но и предоставление чего-либо статического через не-Apache-сервер, такой как Nginx, оптимизированный для обслуживания статического контента. Вы помещаете слой Nginx перед вашим Apache, говорите ему перехватывать запросы на статические ресурсы (например, *.jpg*.png*.mp4*.html Такая настройка называется обратным прокси-сервером (также иногда идентифицируемой с помощью программного балансировщика нагрузки — см. Ниже), и вы можете узнать больше о ее реализации здесь .

Тем не менее, нет ничего лучше, чем масштабирование.

Масштаб

Существует два типа масштабирования — горизонтальное и вертикальное.

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

Вертикальное масштабирование

Представьте себе сервер, обслуживающий рассматриваемое веб-приложение. Сервер имеет 4 ГБ оперативной памяти, процессор i5 и жесткий диск объемом 1 ТБ. Он хорошо выполняет свою функцию, но чтобы лучше переносить больший приток трафика, вы решаете заменить 4 ГБ ОЗУ на 16 ГБ, установить процессор i7 и добавить гибридный накопитель PCIe SSD / HDD. Сервер теперь намного мощнее и может выдерживать более высокую нагрузку. Это называется вертикальным масштабированием или «масштабированием» — вы улучшили машину, чтобы сделать ее более мощной. Другими словами, это происходит:

Горизонтальное масштабирование

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

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

Существует два основных типа балансировщиков нагрузки — аппаратный и программный. Программные балансировщики нагрузки устанавливаются на обычном компьютере и принимают весь трафик, направляя его на соответствующий обработчик. Nginx может быть одним из таких балансировщиков нагрузки в приведенном выше разделе «Оптимизация» — он перехватывает запросы на статические файлы и обслуживает их самостоятельно, не обременяя Apache ими. Еще одним популярным программным балансировщиком нагрузки является Squid , который я лично широко использовал в своей компании, и который обеспечивает действительно глубокий контроль всех аспектов через удобный интерфейс.

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

При горизонтальном масштабировании это происходит:

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

Проблемы с обменом данными

Есть несколько хитрых проблем, которые нужно решить при масштабировании PHP-приложений. Одной из таких проблем является узкое место в базе данных (то, что мы рассмотрим в части 2), а другая — управление данными сеанса — несомненно, если вы войдете в систему на одном компьютере, вы выйдете из системы, если балансировщик нагрузки перенаправит вас на другой компьютер в следующем. запрос, верно? Есть способ обойти это — вы можете поделиться локальными данными между машинами или использовать постоянный балансировщик нагрузки.

Постоянный балансировщик нагрузки

Постоянный балансировщик нагрузки запоминает, куда он ранее перенаправил клиента, и делает то же самое при следующем запросе. Поэтому, если я захожу на SitePoint и захожу в систему, балансировщик нагрузки перенаправляет меня, скажем, на Server1, запоминает меня, и мой следующий щелчок после входа в систему также будет перенаправлен на Server1. Естественно, все это происходит прозрачно. Что, если Server1 выйдет из строя, хотя? Да, все данные сеанса потеряны — я вышел из системы, и мне нужно начать заново на другом сервере. Это ненужное прерывание взаимодействия с пользователем. Более того, балансировщику нагрузки теперь так много нужно сделать (не только перенаправить сотни тысяч людей на разные серверы, но и запомнить, куда он отправил каждый из них), он стал узким местом и может выиграть от некоторого масштабирования самостоятельно. , Но если происходит сбой одного LB, потерянные ли запомненные данные о клиентах и ​​серверах, на которые они были отправлены? КТО СМОТРЕТЬ ЧАСОВ? Ситуация пахнет очевидным уловом 22.

Обмен локальными данными

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

Теперь мы знаем, что данные сеанса хранятся в суперглобальном файле $_SESSION$_SESSION Однако если указанный диск находится на одном сервере, очевидно, что другие серверы не имеют к нему доступа. Как же тогда сделать его доступным на нескольких машинах?

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

Использование базы данных

Используя пользовательский обработчик сеанса, мы можем убедиться, что данные сеанса всегда хранятся в базе данных. База данных должна находиться на отдельном сервере (или в отдельном кластере!), Поэтому серверы с балансировкой нагрузки по сравнению с исходной историей служат только бизнес-логике. Хотя этот подход часто работает хорошо, при действительно высоких дорожных происшествиях база данных становится не только единственной точкой отказа (теряйте ее, и вы потеряли все), но также приводит к значительным издержкам соединения из-за необходимости подключения к различным серверам при записи сеанса. данные к нему всегда. Это становится новым узким местом и может использовать некоторое масштабирование, что является еще одной проблемой при использовании традиционных баз данных, таких как MySQL, Postgre и аналогичных (рассматривается в части 2).

Использование общей файловой системы

Может возникнуть желание настроить сетевую файловую систему, в которую все серверы могут записывать свои данные сеанса. Не. Это абсолютно худший подход, подверженный коррупции и сбоям данных, и он чрезвычайно медленный. Это также единственная точка отказа, очень похожая на аспект базы данных выше. Активировать его так же просто, как изменить значение session.save_pathphp.ini Если вы действительно настаиваете на использовании общей файловой системы, гораздо лучше использовать решение, подобное GlusterFS .

Memcached

Вы можете использовать memcached для хранения данных сеанса в оперативной памяти. Это, возможно, небезопасно, поскольку данные в memcached перезаписываются по мере исчерпания свободного пространства, и нет постоянства — запоминание чьего-либо имени пользователя будет длиться только до тех пор, пока работает сервер memcached или есть место для его запоминания. Вы можете быть удивлены, но не является ли ОЗУ отдельным на каждой машине? Как это относится к кластеру? Memcached обладает возможностью виртуального объединения доступной оперативной памяти с нескольких машин в одно большое целое.

Courtesy of memcached.org

Чем больше у вас машин, тем больше становится пул, когда вы выделяете на него дополнительную оперативную память. Вам не нужно отдавать ОЗУ машины в пул, но вы можете, и вы можете дать произвольную сумму от каждого. Таким образом, хороший кусок оперативной памяти остается на машине для регулярного использования, а остальная часть отводится для кэширования, что помогает вам не только хранить данные сеанса в кластере, но и позволяет кэшировать другой контент по своему усмотрению — при условии, что есть номер. Memcached — отличное решение, и этот подход получил широкое распространение.

Использование в приложениях PHP так же просто, как изменение некоторых значений php.ini

 session.save_handler = memcache
session.save_path = "tcp://path.to.memcached.server:port"

Redis Cluster

Redis — это хранилище данных NoSQL в памяти, очень похожее на Memcached, но поддерживает постоянство и более сложные типы данных, чем просто пары ключ => значение на основе строки. У него пока нет поддержки кластера, поэтому его реализация в решении по масштабированию HZ не так проста, как можно подумать, но она достигается. Фактически, альфа-версия их кластерного решения уже выпущена и может использоваться: http://redis.io/topics/cluster-tutorial . Более подробное сравнение Memcached и Redis см. В этом ответе StackOverflow . По сравнению с типичным решением для кэширования, таким как Memcached, Redis больше похож на базу данных Memcached, оказалось правильной.

Другие решения

  • ZSCM от Zend является альтернативой, но требует Zend Server на каждом узле в кластере.
  • Другие хранилища NoSQL и системы кэширования будут работать — попробуйте такие решения, как Scache , Cassandra или Couchbase , все они невероятно быстрые и надежные.

Вывод

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

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