Один день под нагрузкой на irk.ru

Как я справлялся с нагрузкой в 4000 одновременных посетителей на сайте из яндекс.дзена

Только что мне написал главный редактор irk.ru о том, что на сайте находится 4000 человек. Я с остороженностью отношусь к цифрам больше 2000 - это уже серьезная нагрузка на сервер.

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

На irk.ru 3371 посетитель одновременно

Что же первым делом я делаю, когда чувствую, что сайт может лечь?

Этим я и хочу поделиться с вами в этой заметке. Первым делом я захожу на сервер, который принимает входящие соединения от пользователей. Там крутится nginx, который без устали обрабатывает запросы и который является первым рубежом на пути трафика.

Ngxtop

Я запускаю ngxtop - эта программа в реальном времени показывает статистику из аксесс-лога nginx.

Боевой вывод ngxtop

Тут сразу же видно, на какие адреса идет больше всего запросов и какие ответы видят пользователи по этим адресам. В идеале ошибок 4xx и 5xx быть не должно.

Видно, что трафик идет на страницы:

У нас бекенд настроен так, что в случае экзепшна показывает пользователю ошибку 404. Поэтому ошибки 4xx на скриншоте - на самом деле какие-то сбои в процессе работы приложения под нагрузкой.

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

Кстати, ngxtop показывает только данные из лога. Поэтому если у вас для какого-то location прописан access_log off - то статистику вы не увидите. Не отключайте логи ради оптимизации.

Дальше.

Смотрим error log

sudo tail -f /var/log/nginx/error-irk.log

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

Еще одна интересная особенность этого лога - там может быть что угодно. Потому что обычно рвется там, где ты совсем не ждешь.

В моем логе я сегодня увидел гору вот таких ошибок:

2020/04/10 13:07:55 [error] 14362#14362: *264689442 upstream timed out (110: Connection timed out) while connecting to upstream, client: 176.209.67.22, server: ww
w.irk.ru, request: "POST /hydra/material/scroll_depth/ HTTP/1.1", upstream: "http://10.0.0.2:8086/hydra/material/scroll_depth/", host: "www.irk.ru", referrer: "ht
tps://www.irk.ru/news/articles/20200410/daughter/?utm_referrer=https%3A%2F%2Fzen.yandex.com"

Видно, что один из внутренних сервисов, который логирует глубину просмотра страницы, не справляется с нагрузкой. Позже я увидел, что на бекенд шло 220 запросов в секунду. Очевидно, что один воркер торнадо не успевал их все обработать, поскольку на каждый он тратил около 5 мс (что невероятно быстро). Надо будет добавить воркеров.

Так же видно, что наша статья попала в Яндекс.Дзен. Это очень здорово.

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

Перенастроим nginx:

location ~* ^/hydra/(.+) {
    return 200;
}

И заставим его перечитать конфиг: sudo nginx -s reload

Посмотрим на бекенд

В моей схеме подключения, сервер nginx передает запросы на uwsgi-воркеры на бекенде. Как себя чуствует бекенд?

Зайдем и выполним sudo htop:

Ох.

24-ядерный сервер загружен почти почти до предела. Еще чуть-чуть и он перестанет успевать обрабатывать запросы.

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

Nginx cache

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

Пора опробовать мои наработки в деле.

Я сделал отдельный файл /etc/nginx/conf.d/irk.ru.ext.conf вот такого содержимого:

uwsgi_cache irk;

# ключ зависит от куки irk
uwsgi_cache_key "$request_method|$scheme|$proxy_host|$uri|$args|$cookie_irk";
uwsgi_cache_valid 200 10s;

uwsgi_ignore_headers "Set-Cookie" "Vary" "Cache-Control" "Expires";
uwsgi_hide_header "Set-Cookie";

add_header X-Cache-Status $upstream_cache_status;

И подключил его на самые загруженные страницы:

# статья попала в дзен
location /news/articles/20200410/daughter/ {
     include conf.d/irk.ru.ext.cache;
     uwsgi_cache_valid 200 60s;
     include uwsgi_params;
     uwsgi_pass backend;
}

# кеш главной страницы
location = / {
     include conf.d/irk.ru.ext.cache;
     uwsgi_cache_valid 200 60s;
     include uwsgi_params;
     uwsgi_pass backend;
}

# кеш всех новостей сегодня
location ~ ^/news/20200410/([a-z0-9]+)/ {
     include conf.d/irk.ru.ext.cache;
     uwsgi_cache_valid 200 60s;
     include uwsgi_params;
     uwsgi_pass backend;
}

Нагрузка на бекенд тут же упала:

С 2000 до 1500 запросов в минуту

Может показать незначительным, но для сервера значение в 2000 rpm - пороговое. По опыту помню, что за ним начинаются нехорошие вещи. Нужно держаться от него подальше.

Посмотрим на ngxtop спустя полтора часа работы:

Как видите, закешированные страницы не выдали ни одной ошибки 400 и 500. В то время как некешированная страница /news/ выдала 104 ошибки на 8487 запросов - 1.2%.

Bonus

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

Если такая страница попадет в кеш, то каждый посетитель увидит мой аватар в шапке. Как же быть?

Если вы внимательно посмотрите конфиг выше, то обратите внимание, что у нас ключ кеширования зависит от куки $cookie_irk.

Каждый авторизованный пользователь имеет свой уникальный ключ сессии в куке с названиемirk. И для каждого пользователя мы кешируем страницу на 60 секунд. Если я зайду сейчас и через 10 секунд, то первый раз страница откроется с бекенда, а второй раз - из кеша.

Но в чем же смысл? Разве есть прирост производительности в такой схеме?

Есть!

Дело в том, что из дзена приходит массовый наплыв неавторизованных пользователей. А для них у нас сессия не создается. И кука не ставится. Поэтому хоть тысяча, хоть две - все они увидят одну страницу из быстрого кеша за 20 мс

А мне пора заканчивать.

Final

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