5 уроков для разработчиков высоконагруженных систем

С 2010 года мы разрабатываем сервис для организации совместной работы и управления процессами. Сейчас в нашей системе Pyrus работают тысячи организаций и десятки тысяч пользователей. За 4 года мы наработали неплохой опыт обеспечения надежности и хотим поделиться им с вами.

1. Сломаться может все


Мы стараемся подстелить соломку везде, где только можно. Все сервера базы данных 100% зеркалируются. В дата-центрах бывают регламентные работы по 2-3 часа, в это время наш сервис не должен прерывать работу. Поэтому зеркала расположены в разных дата-центрах, и даже в разных странах. Кроме того, на серверах необходимо регулярно устанавливать обновления безопасности, а они временами требуют перезагрузки. В таких случаях горячее переключение на резервный сервер оказывается как нельзя кстати.

В серверах стоят RAID, мы делаем ежедневное резервное копирование. У нас несколько серверов приложений, это обеспечивает масштабирование и позволяет обновлять их по очереди, не прерывая обслуживания. Для балансировки нагрузки мы используем механизм round robin DNS. Мы предполагали, что DNS – это самая надежная система Интернета, ведь без нее ни один сайт не откроется. Однако тут нас поджидал сюрприз.

Мы хостили доменную зону у крупного регистратора register.com, он обслуживает более 3 млн доменов. Как полагается, у нас 2 независимых сервера доменных имен (nameserver), что защищает от сбоя одного из них. Однажды утром отказали оба. Консоль управления register.com была недоступна. В Twitter начали появляться робкие жалобы пользователей, которые через час сменились лавинообразным потоком воплей, плача, стенаний и обещаний немедленно покинуть этого провайдера. Как только он включит сервера обратно.

С тех пор мы перенесли нашу доменную зону в Amazon, который предоставляет 4 сервера доменных имен, расположенных в разных корневых зонах Интернета: .com, .net, .org, .uk. Это дает дополнительный уровень надежности: даже если вся доменная зона .com будет по каким-то причинам недоступна в DNS, клиенты все равно смогут работать с нашим сервисом.

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

2. Вы не знаете, где узкое место вашего приложения


По мере роста нагрузки мы постоянно делаем 2 вещи: покупаем память (RAM) и оптимизируем приложение. Но как понять, какая функция в приложении работает недостаточно быстро? По синтетическим замерам в тестах на машине разработчика судить сложно. Запускать профайлер на боевом сервере практически невозможно – он добавляет слишком много накладных расходов и сервис начинает тормозить.

Приходится вставлять в код контрольные точки и оценивать скорость приложения по времени исполнения программы между ними.

Так мы выяснили, что 1/3 процессорного времени уходит на… сериализацию: упаковку структур данных в JSON-строки. Изучив альтернативные библиотеки сериализации, мы приняли непопулярное решение: написать свою. Реализация для конкретно наших задач работала в 2 раза быстрее самого быстрого доступного на рынке альтернативного решения.

Кстати, многие ошибочно полагают, что шифрование затрачивает много ресурсов процессора. Ранее действительно этот процесс мог «съедать» до 20% ресурсов CPU. Однако начиная с архитектуры Westmere, запущенной в январе 2010 года, команды алгоритма шифрования AES включены в набор команд процессоров Intel. Поэтому переключение с HTTP на HTTPS практически не изменяет нагрузку на процессор.

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

3. Тестируйте все


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

Оказалось, процедура в некоторых случаях начинает перестраивать кластерный индекс в таблице, размер которой на тот момент был около 1TB. Мы не заметили этого, потому что проводили тесты на маленькой таблице. Пришлось, не дожидаясь окончания работы процедуры, запустить сервис. На нашу удачу все основные функции работали корректно, хотя и несколько медленнее обычного, за исключением прикладывания файлов к задачам. Процедура окончила работу через пару часов и 100% работоспособность была восстановлена.

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

4. Скорость тестирования должна быть высокой


Мы выпускаем обновления приложения каждую неделю. Пару раз в год, несмотря на тестирование, в релиз вкрадывается ошибка, небольшая, но неприятная. Обычно такие ошибки обнаруживаются в течение 10 минут после релиза. В таких случаях мы выпускаем исправление (hotfix).

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

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

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

5. Каждая функция продукта должна использоваться


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

Хороший садовник обрезает молодые побеги каждую весну и формирует правильную, здоровую и красивую крону дерева.

Есть ли в вашем продукте функции, которые не использует никто? В Pyrus мы таких не знаем.

Эмпирически мы выработали правило: каждую фичу использует минимум 2% пользователей. Это означает, когда мы отключаем функцию, десяткам или сотням людей это не нравится. Мы всегда предоставляем другой способ сделать то же самое, но привычка оказывается сильнее.

Вывод: развитие требует некоторых жертв. Представьте себе, скольким людям не нравится каждое изменение в продуктах Google и Microsoft.
Поделиться публикацией

Комментарии 25

    +6
    мне кажется, или эта статья — пиар-акция вашей компании?
      +5
      Вы так говорите, как будто это что-то плохое.

      А если серьезно — нам нравится делать полезные вещи. Кому-то наша система приносит пользу, а кто-то, мы надеемся, извлечет пользу из этого текста.
        +3
        я ничего против не имею ни против вас, ни против того что вы делаете. Просто прочитав, я понял, что вы — клевые(наверное, так и есть), но ничего нового не узнал — вот, что я имел ввиду, что разделу статья не очень соответствует. ее б лучше в «я пиарюсь»
          +2
          Не увидел пиара конкретного продукта, увидел общие приёмы (уроки) проектирования и цикла разработки на примере собственного продукта. Вполне соотстветствует тематике хабов, мне кажется
      +10
      6. Желтый снег есть нельзя. Обязательно добавьте, это важный урок.

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

      Кто-нибудь читая этот комментарий вспомнит какой был урок №2 не отматывая вверх? То-то же.
        –3
        Спасибо за комментарий. Как вы, наверное, заметили, это пост с пометкой «из песочницы». Т.е. он у нас тут первый. Будем пробовать писать истории в другом формате.
          +6
          Ну там есть полезное про https :)
            0
            Какой был урок №2 и правда не могу, зато могу перечислить все пять не по порядку…
            Хотя безусловно вы правы, суховато…
            +1
            мы приняли непопулярное решение: написать свою


            Для высоконагруженного приложения — это самое популярное решение.
            Там, где идет борьба за миллисекунды нет места библиотекам общего назначения…
              +2
              > Запускать профайлер на боевом сервере практически невозможно – он добавляет слишком много накладных расходов и сервис начинает тормозить
              A/B подход?
                0
                > Запускать профайлер на боевом сервере практически невозможно – он добавляет слишком много накладных расходов и сервис начинает тормозить

                Что-то тут не так. Мы примерно о каких технологиях говорим? apache? tomcat? php?

                Профайлеры бывают разные и способы профилирования существуют разные.
                  0
                  Наиболее точное, построчное профилирование, замедляет выполнение кода примерно в 10 раз. Разумеется, мы используем легковесное профилирование, но оно не всегда дают достаточно точную информацию.
                    0
                    Всё-таки о каких технологиях мы говорим? Надеюсь, Страшного Секрета в этом нет? :)
                  +5
                  Про Pyrus рассказали, а где про обещанные высоконагруженные системы? O.o
                    –14
                    Кто-то громко пукнул в лужу… или показалось?
                      +1
                      Интересно было бы узнать про опыт с JSON-строками. Сам недавно сталкивался с аналогичной ситуацией. Какие решения пробовали, какая получалась скорость?
                        0
                        У нас не было вопроса JSON или какой-либо другой формат данных. Это обусловлено тем, что основным клиентом сервиса является браузер, в котором есть нативная поддержка JSON. Да и во всех мобильных платформах есть много JSON-библиотек.

                        Вопрос скорее был такой: куда уходит CPU веб сервера и что оптимизировать в первую очередь. Оказалось, что наш собственный код отъедал не более 30%, остальное — gzip и сериализация ответов. Мораль: вначале замеряем, а уже потом ввязываемся в благородное дело кодирования.

                        Цифры: в самых тяжелых запросах сериализация выполнялась более 400мс (около 8Мб данных до gzip). Нам удалось сократить это до 98мс, но не в общем случае, а конкретно для нашего набора данных. Самая быстрая библиотека общего назначения давала около 200мс.

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

                        В Google есть много ссылок на сравнения библиотек. Да и на Хабре, кажется, писали обзоры.
                          0
                          Это же какой json вы отдавали клиенту, если на сериализацию его серверу понадобилось более 400 мс? Что-то здесь не так
                            0
                            Написано же — 8МБ, если структура JSON достаточно сложная, то вполне.
                              0
                              если вы отдаете 8мб json клиенту — значит что-то не так в консерватории, не делайте так вместо переписывания сериализации
                                0
                                Верно, это и написано выше — мы решили двигаться в сторону уменьшения объема данных.
                        +2
                        >>> Так мы выяснили, что 1/3 процессорного времени уходит на… сериализацию: упаковку структур данных в JSON-строки.

                        JSON действительно не самый быстрый формат сериализации. Я лично пользуюсь msgpack (http://msgpack.org/). По моим тестам на луа он примерно в 2 раза быстрее json. Реализация этого сериализатора есть и на пхп и на других языках программирования. Возможно кому-то будет полезно.
                          –3
                          что значит «быстрый формат»? быстрой или не быстрой может быть только реализация
                          0
                          Реализация для конкретно наших задач работала в 2 раза быстрее самого быстрого доступного на рынке альтернативного решения.

                          Вы рассматривали альтернативные форматы? Если да, то какие известные форматы дали большую производительность чем JSON? В итоге в каком виде сериализуются ваши данные?
                            0
                            Мы не рассматривали альтернативные форматы, поскольку JSON — стандарт, поддерживается всем браузерами и библиотеки доступны на всех мобильных платформах. В итоге все в JSON и останется в нем в обозримом будущем.

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое