19 июня 2009 в 02:17

Умные сессии

PHP*
Решаем проблемы производительности.

Исходные данные.
Транслируем футбольные матчи через интернет. Посетителей: в обычные дни 5 000 — 10 000, в дни матчей 100 000 — 150 000.

В дата-центре
  • 5 веб-серверов с апачем и ПХП, наружу вывешены через аппаратный балансировщик загрузки
  • 2 пула мемкеша: для сессий и данных из веб-сервисов

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

Решения
  • Наращивание сети до 1Гбит после окончания серии матчей
  • Увеличение времени хранения кеша (как временная мера)
  • Использование технологии умных сессий

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

В чём суть технологии

  1. Мы не создаём сессию, если она реально не нужна.
  2. Мы не обновляем сессию в хранилище, если она не изменилась.

В самом деле, сайт у нас платный, даже если у нас 150 000 уникальных в сутки, то людей готовых заплатить реально мало. Посетители распределяются примерно следующим образом:
  • 70% — Зеваки. Открыли главную страницу, поняли что не то, что нужно, закрыли. То есть SЕО поработал хорошо, а нам разгребай.
  • 28% — Фанаты. Денег нет, но пользуются нашим сайтом, чтобы следить за информация по ходу матча (голы, замены). Обычно не регистрируются, ибо информация доступна и так.
  • 2% — Реально смотрят матчи, зарегистрировавшись и заплатив.


Реализация

Поскольку у нас используется мемкеш, то у нас уже есть объект Session, который перегружает стандартный функционал сессий и позволяет хранить последнии в мемкеше. Дополнительно мы делаем следующее:
  • Глобальный session_start() помещаем в if, чтобы сессия не создавалась, когда не просят.
    if(!empty($_COOKIE[ini_get('session.name')]))
    session_start();
  • В месте, где пользователя логиним добавляем старт сессии, ибо здесь уже надо:
    if(empty($_COOKIE[ini_get('session.name')]))
    session_start();
  • И наконец в нашем объекте создаём дополнительную статическую переменую, где храним содержимое сессии при чтении, а при записи делаем следующую проверку в самом начале:
    if(self::$data !== null && self::$data == $currentData)
    return strlen($currentData);


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

Коментарии приветствуются.

UPD: Суть поста показать не какой я крутой, а в том, что в ПХП есть мелочи настолько обыденные, что мы перестаём обращать на них внимание, а ведь иногда они не слишком продуманы и оказывают влияние на производительность.
Андрей Нехайчик @gnomeby
карма
43,2
рейтинг 0,0
Самое читаемое Разработка

Комментарии (70)

  • +15
    Исправили косяки программистов, система стала работать лучше – нормально…
    • +4
      Фишка в том, что это неочевидный косяк и общий для всех ЦМС и фреймворков. А общий потому, что логика сессий для большинства ПХП программистов — набор правил не подлежащих изменению.
    • +5
      интересно, не программисты ли писали модифицированный механизм работы с сессиями? ;)
      молчу, что по-умолчанию, разработчики PHP его написали неоптимальным. :)

      На самом деле все происходит так:
      1. Если опыта нет — то это делают, как правило, когда упираются в сеть/диск/БД.
      2. Если опыт есть — то сразу пишут объекты работы с сессиями (и прочие объекты), чтобы они срабатывали только по необходимости, потому что опытный программер задумывается над тем, что будет происходить с системой в каждой строчке его кода.

      PS: возможно для кого-то будет тайной но в некоторых задачах оптимальным является даже кеширование объекта на уровне PHP на момент генерации объекта, в силу того, что он может быть задействован во многих блоках и/или вычислениях. Это и многое другое дает значительный выигрыш производительности системы в целом.
      • 0
        пардон, опечатался…
        «на момент генерации объекта» нужно читать как «на момент генерации страницы»
  • +1
    Умные — это громко сказано.
    • +3
      По крайней мере не такие тупые, как в стандартном варианте большинства фреймфорвок где сессия тупо стартует всегда и не понятно что там делает половина бесполезных данных.
  • +6
    Умные сессии? Скорее, исправление плохого кода ;).
    • 0
      Нифига =)

      • 0
        Пардон.

        >>> if(self::$data !== null && self::$data == $currentData)
        Оператор "!==" сравнивает по значению и типу. А на какой тип стоит проверка именно здесь?
        И потом, автор проверяет сначала на непустоту self::$data и на равенство некоторой переменной. Ы?
        • 0
          >> А на какой тип стоит проверка именно здесь?
          На отсутствие null естественно, ибо (false !==null) != (false !=null)

          >> автор проверяет сначала на непустоту self::$data и на равенство некоторой переменной
          Естественно, ибо эта переменная в первый раз тоже может содержать null.

          >> И потом,
          И потом мне кажется, вам просто делать нечего придираясь к таким местам, статья описывают идею, не более.
  • –19
    Хвастаемся своим участием в большом проекте? :)
    • +4
      Нет, просто небольшая заметка по оптимизации, особенно пригодится параноикам.
  • –3
    У вас нет сисадмина. Был бы — вместо апача давно nginx стоял.
    • +4
      На стороне заказчика — целый отдел. Поскольку они америкосы — nginx они видали в гробу.
      • –4
        они просто тупые, так как nginx обсуждали еще на найскайлабилити блоге и многие используют.
      • 0
        Lighty?
        • +1
          Может быть дело бы и дошло, но апач сервера их полностью устраивали. Логика была простой, если апач сервера не справлялись с нагрузкой, то в пул добавлялся ещё один веб-сервер, ибо это операция дешёвая.
          • 0
            >ибо это операция дешёвая.
            деньги есть — ума ненадо…
      • –2
        Сказочные долбоебы
      • +2
        Мне как-то сказали — «nice, but give me docs!»
        Надо бы собраться и сделать уже Сысоеву в подарок мультиязычный красивый сайт для nginx!
        • –2
          Лучше пусть пиндосы учат русский.
        • 0
          Нашел!
          wiki.nginx.org/Main
  • +12
    Эх, если бы настоящие проблемы решались расстановкой двух ифов…
    • 0
      имхо если была бы нормальная система аутентификации и авторизации, например с применением AOP, то и заплаток что тут описывается не было бы… это не решение всех проблем, но решение многих 8)
  • –4
    а если кука есть, но просроченная?
    • +2
      просроченную куку бровзер не посылает
      • 0
        просроченная не для браузера а для приложения
        • +1
          Значит надо сделать что бы они одновременно просрачивались :)
          • +6
            просрачить
            срачить
            отсрачить
            пересрачить
            досрачить
            недосрачить
            перенедопросрачить

            отличное слово, осталось придумать его значение :)
            • +5
              Ага. Есть же красивое и понятное русское слово «заэкспайрилась»
              • +1
                а есть еще более симпатичное — «истек строк»
  • НЛО прилетело и опубликовало эту надпись здесь
    • НЛО прилетело и опубликовало эту надпись здесь
  • +4
    >То есть СЕО поработал хорошо, а нам разгребай.
    у вас что, за поисковые оптимизации отвечает ген. директор? :)
    • +1
      Случается…
    • –2
      чтобы не переключать раскладку SEO часто СЕО называют, так как в рунете СЕО, тот который большой дирехтор, не используется.
  • 0
    А сайт в личку можно
    • +4
      Раз 150 тысяч, то можно уж комментарием. Думаю многим будет интересно.
      • +1
        Можно: qualifiers.jumptv.com/

        В текущей версии сессии для гостей уже вернули назад, поскольку появилось требование поддерживать бесплатный просмотр для гостей лучших событий в матче, а статья описывает события годичной давности, так что просьба не пинать.
        • +4
          щас по нему хабрапрофилировщик пройдется )
        • 0
          Лохотрон какой-то. Выбрал матч Chile — Bolivia, посмотрел рекламу, потом на сообщение «Video not available», и опять рекламу. Отличный сервис!
          • –1
            Сейчас там китайцы всем заправляют (купили компанию и поменяли нас на свой офшор), так что что у них происходит — неясно.
        • 0
          >сессии для гостей уже вернули назад
          * Наращивание сети до 1Гбит после окончания серии матчей?
          * Увеличение времени хранения кеша (как временная мера)
          * Использование технологии умных сессий
          • 0
            йо майо strike не сработал!
  • +2
    Ну вообще логично не открывать сессию там, где она не нужна (как не удивительно).
  • НЛО прилетело и опубликовало эту надпись здесь
  • +3
    «В месте, где выводится форма логина добавляем старт сессии, ибо здесь уже надо» — всегда думал, что сессию нужно открывать ПОСЛЕ успешного логина.)
  • 0
    Скоро на Хабре будут статьи вида «Как запустить сессию?», с текстом — «Используйте session_start()».
    • –1
      Это типа плохо, да? Все из известного места профи выходят?
  • 0
    www.lexa.ru/nginx-ru/msg06037.html и не насилуйте себе мозг.
    Кроме того отдавать видео с апачей — это ужас-ужас-ужас.
    • +1
      Видео отдаётся с CDN.
      • +4
        Про CDN Ваш могли бы рассказать, было бы интересно.
        • –1
          +500
        • +2
          Ну это не наш, это компании заказчика (а с недавнего времени и бывшего, ибо нашего заказчика купили и всё там реорганизовали). + его структура слишком не идеальна, чтобы им можно было хвастаться.

          Но суть такая:
          * пачка WMS на базе Windows Server 2003 в 5 континентах. В каждом подразделении своя служба поддержки. Очень часто отваливается видео и звук, поэтому дополнительно наняты девочки, которые проверяют, в том числе визуально, что всё хорошо.
          * для WMS написан плагин для авторизации, которому ПХП передаёт различные хеши, а он уже решает пускать пользователя или нет.
          * Дополнительно ПХП совершает ряд действий по определению ближайшего дата центра, чтобы пользователям было лучше.

          Соответственно сам CDN требует очень больших затрат хотя бы по персоналу.

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

          Но тем не менее мой ему поклон, ибо на основном сайте в течении нескольких лет можно посмотреть около 250 каналов.
  • 0
    Сайта не будет?
    • +1
      Все — увидел…
  • +4
    5 серваков, аппаратный балансировщик и всего 150 000 посетителей. Нас бы уволили за такое.
    • +1
      согласен.
      у нас nginx, за ним 5 php бэкендов, один мемкеш, 1 mysql + 1 slave
      и все это вытягивает 10M страниц в день (примерно 1M посетителей)

      и страница раз в 10 тяжелее по структуре чем в представленном сайте…
    • +1
      Вы узко мыслите. Во-первых правило компании: на сервер приходится не более 20% загрузки, для подстраховки. А во-вторых расходы на сайт и его сервера были мизерными по сравнению с расходами на CDN.
      • –2
        удивительно
      • 0
        Расскажите лучше какую и как используете CDN?
  • 0
    А раз такая проблема с хранением сессионных данных в мемкеше, то может есть смысл всё в COOKIES писать/читать? Не забываем конечно про дополнительный хеш для их валидации.
    • 0
      Не, это жепь. К тому же при переходе на 1Гбит, проблема отпала.
  • –1
    А как же система добавления комментариев от незарегистрированных пользователей с использованием капчи? Или просто-напросто форма обратной связи?

    Мне кажется, что Ваша мысль имеет смысл, но при этом реализована не совсем универсально (в отличие от использования session_start() в главном файле). Всегда нужно иметь под рукой механизм хранения для любого типа пользователей, а не только для авторизованных.
  • –2
    Вы про multicast слышали?
    • 0
      Да, только какое это отношение имеет к топику?
      • –2
        я к тому что, исходя их исходных данных и при решении проблемы производительности, стоит смотреть в сторону более серьезных оптимизаций. А решение в топике больше похоже на копание с миру по чайной ложке.
        • 0
          я с вами не согласен! это универсальное решение. и оно очень сильно снимает «тупую» нагрузку на сервера. я это применяю повсеместно…
          данные подтягиваются из хранилища только в том случае, если они реально понадобились…
          все это в PHP реализуюется легко благодаря ООП.
          если к объекту сессии пользователя небыло никакого обращения, то они останется неизменным и ни читать ни сохранять ничего не будет.
          все это работает и в других местах… надо просто уметь применять.

          назовите, на ваш взгляд, более серьезную оптимизацию.
        • 0
          Мультикаст снимает нагрузку с CDN, которая не имеет практически никакого отношения к рассматриваемой инфраструктуре, кроме того, что юзает общую базу.

          Кроме того вы мультикаст юзали? Вы знаете что там дохрена подводных камней?
  • 0
    Это легко реализовать.

    Например если нужно показать все записи с тегом, то нужно писать «memcache», если все записи с тегом «оптимизация» в которых небыло бы тега memcache, то «оптимизация-memcache», если все записи с тегами «memcache», «оптимизация», «сессии», то «memcache+оптимизация+сессии» ну и далее по смыслу.

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