Это не статья о том, как достичь 5 000 000 ежемесячных просмотров. Это статья о том, как нам удалось спроектировать, разработать, оптимизировать и поддерживать (3,5 года) такой веб-сайт.
Я опубликовал эту статью в своем небольшом блоге: webinuse.com . Я стараюсь быть более полезным обществу, публикуя статьи, основанные на моих знаниях и моем опыте.
Начало
К моей компании (я там еще не работал) обратился клиент, который попросил нас создать простой новостной/журнальный портал, на котором будут работать 1-2 человека и который будет самодостаточным (загруженным).
После первоначальных переговоров они согласились начать этот проект, и мой коллега был назначен для этого проекта. У клиента было только одно требование: это должно было быть сделано с нуля с использованием PHP, MySQL и “простого JavaScript”. “Причина этого заключалась в том, что двоюродный брат какого-то клиента знал “некоторых из них”, поэтому он сможет помочь им, если им нужно что-то изменить.
Этот коллега, назовем его Джон, начал с нуля и закончил примерно 60-70% проекта, когда я присоединился к команде.
Через несколько недель мы смогли представить их (у него уже работало 2 человека) с помощью простой CMS и портала, и после еще нескольких настроек мы начали работать. (До этого у нас была куча встреч и исправлений, но это не имеет отношения к истории.)
Это был простой веб-сайт, простая CMS для создания постов и галерей. Они могли вставлять изображения, менять пользователей и управлять фотографиями. По просьбе клиента (совет кузена) мы разместили этот веб-сайт на OVH, и они начали публиковать статьи и выполнять свою работу.
Первые проблемы
Через несколько месяцев они заметили, что веб-сайт работает довольно медленно, поэтому они снова позвонили нам (это выходило за рамки его двоюродного брата, и это становилось действительно серьезным проектом), и попросили нас немного оптимизировать веб-сайт в отношении времени загрузки. Благодаря своему опыту, в то время они смогли собрать довольно хорошую аудиторию и переполнить свои серверы. У них было около 150-200 посетителей в любое время, читающих статьи.
Естественно, мы решили, что лучший способ оптимизировать его – масштабировать по вертикали, поэтому мы перешли на гораздо лучший сервер. И какое-то время все шло хорошо. Они также попросили нас добавить множество новых функций, таких как:
- им нужно было контролировать каждую позицию, 62 позиции, на главной странице (какая статья и как долго она будет оставаться на определенной позиции);
- они хотели иметь возможность перетаскивать статью туда, где они хотели, чтобы она была
- они хотели посмотреть, сколько пользователей в любой момент времени находится на их панели мониторинга
- они хотели добавить кучу коротких кодов для разных типов контента и т.д.
Поскольку мой коллега был ведущим разработчиком, генеральным директором и соучредителем, я был как бы вынужден прислушиваться к нему, и мы развивались так, как он требовал. Иногда он прислушивался к моим “советам”, но большую часть времени просил меня сделать так, как он планировал. Честно говоря, мне нужна была эта работа, поэтому я никогда не возражал слишком сильно. Кроме того, он делал 90% бэкэнда, а я делал 100% фронтэнд и 10% бэкэнд.
После того, как мы закончили все эти функции, я просто почувствовал, что каждая функция сделана из стекла, и она не продержится так долго. Итак, я был прав.
Ошибка № 1 заключалась в том, что для каждой функции он использовал бесконечное количество запросов к БД, что делало приложение довольно медленным. Вместо того, чтобы манипулировать некоторыми данными с помощью PHP или JS, он просто извлек их из базы данных.
Ошибка № 2 заключалась в том, что из-за всех этих позиций на главной странице у нас было около 30 запросов к БД только для загрузки главной страницы, что было ужасно.
Ошибка № 3 заключалась в том, что он использовал PDO, но на самом деле он никогда не использовал подготовленные инструкции, проверьте код ниже.
//Instead of using PDO like this
$stmt = $pdo->prepare("SELECT * FROM post WHERE slug = ?");
$stmt->execute([$slugString]);
//He used it like this
$stmt = $pdo->query("SELECT * FROM post WHERE slug = $slugString")
Из-за этой последней ошибки наше приложение было подвержено хакерским атакам, и мы действительно боролись с этим.
Ошибка № 4 заключалась в том, что мы на самом деле не обращали внимания на время загрузки любого типа, так что вы можете себе представить, насколько клиент был доволен.
В этот момент наша домашняя страница загружалась в течение ~5-7 секунд. Но настоящая проблема возникла, когда этот портал сообщал о крупном событии по всей стране, и цифры за считывание выросли за несколько секунд, мы поднялись с 500-600 до 1300 примерно за несколько минут. Все буквально остановилось. Позвонил клиент, он был в ярости, но на данный момент мы действительно ничего не могли сделать.
Средняя фаза
Перенесемся через несколько месяцев, мой коллега покинул компанию и продал ее мне и моему другу, поэтому мы пошли к нашему клиенту и пообещали исправить его ошибки и улучшить приложение настолько, насколько сможем, мы даже согласились сделать проект на общественных началах для клиента (стоимость проекта была довольно высокой, но нам было все равно, мы просто хотели продолжить наши отношения с этим клиентом, потому что он был высокооплачиваемым, действительно хорошим парнем, с ним было/приятно работать).
Итак, мы начали работать. Моя первая цель состояла в том, чтобы заставить домашнюю страницу загружать все только одним или двумя запросами. Для этого я создал новую таблицу, в которой была сохранена только домашняя страница в порядке тех позиций, о которых я упоминал ранее. Мгновенное время загрузки сократилось до 2 секунд, и клиент был очень доволен. После этого я внедрил кэш для всего на сайте, кроме запросов. Таким образом, мне удалось сократить время загрузки до 800 мс-1,5 секунды.
Кроме того, мне пришлось реализовать лучшее сжатие изображений и все такое, но теперь front работал так, как ожидалось, теперь мне приходилось иметь дело с CMS. Проблема заключалась в том, что этот внутренний код вообще не комментировался, даже если он был прокомментирован, это было что-то вроде:
//Get everything from gallery table
$stmt = $pdo->query("SELECT * FROM gallery");
Как вы можете видеть, в этом не было ничего действительно полезного, поэтому мне пришлось пройти строчку за строчкой и попытаться выяснить, что происходит. Мне повезло, что я был там с самого начала, так что большую часть кода я смог понять довольно быстро.
Время загрузки было проблемой на бэкэнде, так что мне тоже пришлось с этим столкнуться. В то время он сказал, что мы должны использовать AJAX для загрузки всего, так что в основном вы получите скелет, а затем мы будем запрашивать данные. И в большинстве случаев данные зависели от каких-то других данных, поэтому у нас была очередь на загрузку, и она была чертовски медленной. Поэтому вместо этого я использовал PHP для загрузки всего (рендеринг на стороне сервера), и теперь приложение загружалось довольно быстро.
Большинство “продвинутых” функций пришлось перезаписать, но в конце концов все заработало.
Сегодня
После всех этих улучшений мы снова сменили хостинг, и теперь у нас довольно мощный сервер. Кроме того, теперь у нас есть резервный сервер с той же спецификацией, что и у основного сервера, который можно использовать либо в качестве резервного, либо мы можем использовать базу данных на одном сервере и файлы на другом, чтобы лучше распределять нагрузку.
Несколько недель назад у нас было одно событие, которое привело к 5000 одновременным пользователям на сервере в течение нескольких часов. Все работало довольно хорошо и без серьезных “сбоев”, но мы увидели, что 5000 – это почти наш предел при существующей инфраструктуре, поэтому мы провели совещание и решили все улучшить.
Согласно статистике, каждый час у нас около 7000-8000 просмотров страниц и 5 000 000 – 5 500 000 просмотров страниц в месяц.
Мы провели некоторые расчеты и решили, что сначала внедрим Redis. Мы уже провели некоторое тестирование с домашней страницей, и время загрузки на самом деле не отличается при меньшем количестве посетителей, но когда их число велико, мы можем видеть различия во времени загрузки и производительности сервера в отношении процессора, Ввод-вывод и использование оперативной памяти.
Несмотря на то, что оперативная память больше “занята” Redis, процессор и ввод-вывод работают намного лучше, поэтому я думаю, что мы продолжим работу с Redis. Кроме того, мы рассматриваем возможность переписывания всего на что-то более производительное, возможно, MERNor Laravel с MySQL и MongoDB. MongoDB будет использоваться для статей. Мы бы сохранили всю статью и сопроводительную информацию для облегчения извлечения.
Как я уже сказал, мне бы очень хотелось услышать ваше мнение обо всем, что касается моей статьи. Спасибо вам за чтение.
Если у вас есть какие-либо вопросы или что-либо еще, вы можете найти меня на моем Твиттере , или вы можете прочитать некоторые старые статьи, такие как консоль.войдите в JavaScript – Советы и рекомендации
Оригинал: “https://dev.to/amersikira/from-0-to-5-000-000-monthly-views-5bj3”