Масштабируемый способ регистрации данных запроса страницы из приложения PHP? - PullRequest
5 голосов
/ 17 мая 2011

Разрабатываемое мной веб-приложение (на PHP) требует возможности регистрировать каждый запрос страницы.

Как и обычный access_log, в нем будут храниться такие данные, как запрошенный URL-адрес, IP-адрес источника, дата / время, но он также необходим для хранения идентификатора пользователя, вошедшего в систему (который хранится в переменной сеанса php).

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

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

Вот несколько идей, которые у меня были:

1) Поработайте над тем, чтобы Nginx мог регистрировать user_id из сеанса / приложения в обычном веб-сервере access_log, который можно периодически анализировать и загружать в базу данных (ночью). Это похоже на хакерство, и его придется выполнять на каждом веб-сервере, когда система масштабируется.

2) Записывать каждый запрос страницы в Redis с высокой скоростью записи - проблема в том, что у вас нет возможности запросить дату позже.

3) Записывать каждый запрос страницы в Memcache / Redis, действующую как кеш (или очередь сообщений), и оттуда он будет регулярно извлекаться, вставляться в MySQL и удаляться.

4) Подойдет ли что-то вроде MongoDB, которое имеет больше возможностей для запросов?

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

Меня также интересуют мысли о том, как можно соответствующим образом структурировать данные для хранения в memcache / redis.

Спасибо

Ответы [ 3 ]

15 голосов
/ 18 мая 2011

Это, безусловно, выполнимо различными способами.Я расскажу о каждой из перечисленных опций, а также о некоторых дополнительных комментариях.

1) Если NGinx может это сделать, дайте это.Я делаю это с Apache, а также с JBOSS и Tomcat.Затем я использую syslog-ng, чтобы собрать их централизованно и обработать оттуда.Для этого маршрута я бы предложил формат сообщения журнала с разделителями, такой как разделенный табуляцией, поскольку это облегчает анализ и чтение.Я не знаю, как он регистрирует переменные PHP, но он, безусловно, может регистрировать заголовки и информацию о файлах cookie.Если вы вообще собираетесь использовать протоколирование NGinx, я бы порекомендовал этот маршрут, если это возможно - зачем регистрироваться дважды?

2) Нет «отсутствия возможности запросить дату позже», более подробнониже.

3) Это вариант, но полезен он или нет, зависит от того, как долго вы хотите хранить данные и сколько очистки вы хотите записать.Подробнее ниже.

4) MongoDB, безусловно, может работать.Вам придется писать запросы, а они не являются простыми командами SQL.

Теперь для хранения данных в redis.В настоящее время я регистрирую вещи с помощью syslog-ng, как было отмечено, и использую место назначения программы для анализа данных и помещения их в Redis.В моем случае у меня есть несколько критериев группировки, например, по vhost и по кластеру, поэтому мои структуры могут немного отличаться.Вопрос, который вам нужно решить в первую очередь: «какие данные мне нужны из этих данных»?Некоторые из них будут такими счетчиками, как скорость трафика.Некоторые из них будут агрегированными, а еще больше будут такие вещи, как «упорядочить мои страницы по популярности».

Я продемонстрирую некоторые приемы, позволяющие легко перенести это в redis (и, следовательно, вернуться обратно).

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

Храните данные в отсортированном наборе, используя ключ «traffic-by-url: URL: YYYY-MM-DD» в этом отсортированном наборе, который вы будете использовать цинкбю и введите элемент "ЧЧ: ММ".например, в Python, где «r» - это ваше соединение с Redis:

r.zincrby("traffic-by-url:/foo.html:2011-05-18", "01:04",1)

В этом примере увеличивается счетчик для URL «/foo.html» 18 мая в 1:04 утра.

Чтобы получить данные за определенный день, вы можете вызвать zrange на ключе ("" traffic-by-url: URL: YYYY-MM-DD "), чтобы получить отсортированный набор изнаименее популярный к самому популярному.Например, чтобы получить топ-10, вы должны использовать zrevrange и указать диапазон.Zrevrange возвращает обратную сортировку, наибольшее количество попаданий будет сверху.Доступно еще несколько отсортированных команд set, которые позволяют вам выполнять приятные запросы, такие как разбиение на страницы, получать диапазон результатов по минимальному количеству баллов и т. Д.

Вы можете просто изменить или расширить имя ключа для обработки разных временных окон,Комбинируя это с zunionstore , вы можете автоматически свернуть до менее детальных периодов времени.Например, вы можете объединить все ключи за неделю или месяц и сохранить в новом ключе, например «Traffic-by-url: month: URL: YYYY-MM».Выполняя вышеупомянутое на всех URL в данный день, вы можете получать ежедневно.Конечно, вы также можете иметь ежедневный ключ общего трафика и увеличивать его.В основном это зависит от того, когда вы хотите, чтобы данные вводились - в автономном режиме с помощью импорта файла журнала или как часть пользовательского опыта.

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

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

Вы также можете хранить необработанные журналы в Redis.Я делаю это для некоторых журналов, хранящих их как строки JSON (у меня они есть как пары ключ-значение).Затем у меня есть второй процесс, который извлекает их и обрабатывает данные.

Для хранения необработанных попаданий вы также можете использовать отсортированные наборы, используя Epoch Time в качестве ранга, и легко захватить временное окно, используя команды zrange / zrevrange.Или сохраните их в ключе, который основан на идентификаторе пользователя.Наборы будут работать для этого, как и отсортированные наборы.

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

Если вам действительно нужны данные в базе данных, попробуйте использовать функцию Redis Pub / Sub, и у вас будет подписчик, который анализирует их в формате с разделителями и создает дамп в файл.Затем запустите процесс импорта, который использует команду копирования (или эквивалентную для вашей БД) для массового импорта.Ваша БД поблагодарит вас.

Последний совет (который я, вероятно, уже потратил на это достаточно времени) - разумно и либерально использовать команду expire .Используя Redis 2.2 или новее, вы можете установить срок действия даже на счетные клавиши.Большим преимуществом здесь является автоматическая очистка данных.Представьте, что вы следуете схеме, описанной выше.С помощью команд истечения вы можете автоматически удалить старые данные.Возможно, вам нужна почасовая статистика на срок до 3 месяцев, затем только ежедневная статистика;ежедневная статистика за 6 месяцев, затем только ежемесячная статистика.Просто истекайте ваши почасовые ключи через три месяца (86400 * 90), ваши ежедневные на 6 (86400 * 180), и вам не нужно будет делать очистку.

Для геотаггинга я делаю автономную обработку IP.Представьте себе отсортированный набор с этой ключевой структурой: «traffic-by-ip: YYYY-MM-DD», используя IP в качестве элемента и используя указанную выше команду цинкрибы, чтобы получить данные трафика для каждого IP.Теперь в своем отчете вы можете получить отсортированный набор и выполнить поиск по IP.Чтобы сэкономить трафик при создании отчетов, вы можете настроить хэш в redis, который отображает IP-адрес в нужное место.Например, «geo: country» в качестве ключа и IP в качестве хеш-члена с кодом страны в качестве сохраненного значения.

Большое предостережение, которое я хотел бы добавить, заключается в том, что если ваш уровень трафика очень высок, вы можете захотеть запуститьдва экземпляра Redis (или более в зависимости от трафика).Первым будет экземпляр write, в нем не будет включена опция bgsave.Если ваш трафик довольно высокий, вы всегда будете делать bgsave.Это то, что я рекомендую для второго экземпляра.Это раб первого и делает сохранение на диск.Вы также можете выполнить свои запросы к ведомому устройству, чтобы распределить нагрузку.

Я надеюсь, что это даст вам некоторые идеи и вещи, которые стоит попробовать.Поэкспериментируйте с различными вариантами, чтобы увидеть, что лучше всего подходит для ваших конкретных потребностей.Я отслеживаю много статистики на веб-сайте с высоким трафиком (а также статистику журнала MTA) в Redis, и она прекрасно работает - в сочетании с Django и API визуализации Google я получаю очень красивые графики.

1 голос
/ 17 ноября 2012

Когда вы используете MongoDB для ведения журнала, проблема заключается в конкуренции за блокировку из-за высокой пропускной способности записи. Хотя вставка MongoDB по умолчанию работает по принципу «забей и забывай», при вызове большого количества insert () возникает серьезный конфликт блокировки записи. Это может повлиять на производительность приложения и помешать читателям объединять / фильтровать сохраненные журналы.

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

Они буферизируют журналы и асинхронно записывает данные в другие системы, такие как MongoDB / PostgreSQL / и т. Д. Запись выполняется партиями , поэтому она намного эффективнее записи прямо из приложений. Эта ссылка описывает, как поместить логи в программу Fluentd из PHP.

Вот несколько уроков о MongoDB + Fluentd.

0 голосов
/ 23 мая 2011

Отправить информацию о регистрации в syslog-ng:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...