Pull to refresh

Как я спасал сайт apachedev.ru

Reading time4 min
Views2.3K
Решил посетить сайт (на который не заглядывал уже несколько лет) посвященный внутренностям apache2. Но увидел лишь сообщение от хостера о том, что сайт временно заблокирован. Сделал запрос в яндексе: в кеше у поисковика уже ничего нет. Думаю, что сайт заблокирован давно. С помощью смс я продлил хостинг на один день и увидел, что обновлений не было с декабря 2007 года. Попытался связаться с автором через электронную почту, указанную в контактах домена. Ответа не получил…



Надо отметить, что со спасаемым сайтом мне повезло. Он простой и логичный. Копировать статьи, картинки — долгий и скучный процесс. Я решил скопировать сразу все. Что мы имеем:

  • Исходный сайт полностью статичный
  • ModRewrite все перекидывает в один php-скрипт
  • Скрипт ищет файл в кеше, если в кеше нет, запрашивает с сайта-источника
  • Решил хранить все в sqlite


В качестве хранилища данных я выбирал между:


Хранить файлы как есть: т.е. если исходный адрес станицы /topic/123.html, то создавать директорию topic и помещать в нее файлы. Например, так делает wget. Но такой подход мне не понравился.

Делать md5-хеш от URI и сохранять в файл data/ТУТХЕШ.db, очень много файлов. Найти потом в этой папке что-либо невозможно. Не понравилось.

Делать md5-хеш и хранить в sqlite базе. По своей сути это, тоже самое, что и предыдущий вариант, но только одним файлом. Рассматривался еще вариант хранения в mysql — но это уж больно не мобильно и громоздко. А sqlite: переписал несколько файлов на новое место — сайт развернут и готов к работе.

ModRewrite — ничего нового я не изобрел:


RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php
# и на всякий случай редирект:
RewriteCond %{HTTP_HOST} !^apache2dev\.ru$
RewriteRule ^ apache2dev.ru%{REQUEST_URI} [R=301,L]


Как все работает — index.php


С исходным кодом можно ознакомиться тут: apache2dev.ru/index.phps

1. Берем $_SERVER['REQUEST_URI'], на всякий случай добавляем адрес целевого сайта,
2. Получаем md5-хеш
3. Смотрим есть-ли запрашиваемая страница в локальном кеше
4. Если нет, запрашиваем на сайте источнике, разбираем заголовки ответа. Нас интересуют только два: 'Content-Type' и 'Last-Modified'
5. Сохраняем в кеше заголовки и ответ. Выдаем клиенту результат, предварительно немного обработав HTML файлы. Критерий Content-Type = text/html
6. Добавляем Expires на сутки.

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

Создаем базу данных:
CREATE TABLE storage (loc TEXT PRIMARY KEY, heads TEXT, fdata TEXT, location TEXT);

Теперь когда я открыл свой сайт и увидел на нем страницу с картинками, первая порция данных была сохранена в локальный кеш.

Запускаю 'wget -r мойсайт.ru' и даю немного поработать. wget с ключом -r пытается рекурсивно скачать весь сайт. У него это получается не очень хорошо. Например, он вообще не знает что такое javascript. Теперь открываю сайт и пробегаюсь по страничкам, чтобы зацепить то, что упустил wget.

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

Исходник: apache2dev.ru/list.phps

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

Замер скорости


Я производил замер с помощью ab: картинка объемом 70кб и произвольная html-страница. Разница лишь в дополнительных preg_replace (я позволил себе вырезать немного рекламы, заменил абсолютные ссылки на относительные и вставил предупреждение, что эта страница — копия)

# ab -c 10 -n 1000 apache2dev.ru/images/ff_adds/validator.gif
Requests per second: 417.32[#/sec] (mean)

# ab -c 10 -n 1000 apache2dev.ru/2006/01/28/ustanovka-apache-20-2
Requests per second: 29.66 [#/sec] (mean)

Результат меня вполне устраивает.

Плюсы и минусы такого подхода:

  • «Спасти» сайт нужно лишь единожды, и потом можно про него забыть. Wordpress я как-то побаиваюсь: за ним нужно следить, обновлять и т.п.
  • Сайт уместился в 7 файлов, удобно копировать; sqlite база — 20мб
  • Требования к системе минимальные
  • Самым главным минусом (по моему мнению) является тот факт, что далеко не каждый сайт можно подобным образом сохранить. Есть сайты гуляя по ссылкам которых, можно не остановиться никогда.


Возможные улучшения:

  • В базе данные можно было хранить уже в обработанном виде (preg_replace и проч.)
  • А еще данные можно было хранить в сжатом виде. Проверять Accept-Encoding: если там стоит gzip — выдавать как есть, иначе распаковывать
  • Если понадобиться высокая производительность, можно попросить nginx кешировать данные, выдаваемые php скриптом
  • Предварительно подготавливать URI страницы. Например, удалять всякие ?from=top10, ?from=ap2.2, сейчас одинаковые страницы (но с разными адресами) сохранены по нескольку раз.
  • Передавать через context в функцию file_get_contents параметр max_redirects=0, обрабатывать ошибку и сохранять заголовок Location. Сейчас пользователь запросив /get.php?=/download/123.pdf получит сразу данные, как будто бы именно по этому адресу и находится pdf файл, т.к. функция file_get_content (в нашем файле index.php) увидев редирект, автоматический его выполнит, никому не сказав ни слова. А по идеи-то пользователь должен получить ответ от сервера HTTP/1.1 302 Found, и сделать уже сам еще один запрос непосредственно к файлу.


Кстати, сайт про apache, о котором идет речь: apache2dev.ru
Полный комплект: apache2dev.ru/catcher.tgz
Tags:
Hubs:
Total votes 106: ↑90 and ↓16+74
Comments30

Articles