Author Archives: cronfy

Ограничение количества файлов на хостинге: через количество к качеству

Задачей хостинг-провайдера является оказание качественных услуг по размещению сайтов. Эта задача достаточно удобно решается в случае, когда каждый клиент использует для размещения своих проектов выделенный сервер или хотя бы VDS. Обеспечение высокого качества предоставляемых услуг на shared-хостинге сопряжено с рядом дополнительных задач: важно, чтобы ресурсов сервера было достаточно для комфортной работы сайтов всех размещаемых на нем клиентов. Для решения этих задач ресурсы тщательно учитываются, и этот учет подразумевает их количественную оценку.

Читать далее

HelpDesk — какую выбрать систему поддержки пользователей?

В адрес технической поддержки и клиентской службы нашей компании ежедневно поступает большое количество писем. Сначала для их обработки мы использовали просто Thunderbird, цветные метки и звездочки. Когда корреспонденции стало больше, потребовалось систематизировать работу с почтой: нужна была возможность оставлять друг другу комментарии, распределять задачи между сотрудниками, в удобном виде отображать историю переписки, использовать заготовки ответов и многое другое.

Подобные возможности предоставляют так называемые системы HelpDesk. Заявка в такой системе, как правило, называется «тикетом», поэтому систему по-русски можно назвать «тикетницей». Для поиска решения, наилучшим образом подходящего для нас, мы составили список требований, посмотрели чуть более 100 продуктов, 16 из них установили и протестировали, определили финалистов и, наконец, выбрали ту систему, которую мы используем сейчас.

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

Начнем с самого интересного — с финалистов. Читать далее

Замена хотсвопом диска в зеркале gmirror под FreeBSD

Ситуация: есть RAID-массив из двух SATA-дисков в зеркале, созданном с помощью gmirror под FreeBSD.

Необходимо заменить один диск, не останавливая работы сервера.

Капля теории

На тему собственно создания зеркала на GEOM есть много статей.

При создании зеркала gmirror синхронизирует все данные, включая MBR, гласит http://people.freebsd.org/~rse/mirror/ (раздел Summary -> GEOM mirror on whole disk). Если при отказе диска сервер умер, можно загрузиться с оставшегося диска, вне зависимости от того, какой диск вышел из строя. Важно только при загрузке правильно выбрать, с какого диска грузиться.

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

# gmirror list

практически постоянно показывает Flags: DIRTY. Это нормально: флаг выставляется, когда на диск записывается информация, и в этот момент состояние данных на дисках массива не совпадает. Если на диск постоянно ведётся запись, флаг DIRTY постоянно будет выставлен.

Процедура замены

Предположим, в массиве gm0 присутствуют два диска: da0 и da1. Заменить нужно da0.

  1. Определяем, какой диск физически нужно заменить.
  2. Не выключая сервер, вытаскиваем диск.
  3. Сервер несколько секунд ничего не понимает, а потом на полминуты впадает в кому. Нужно немного подождать.
  4. После этого команды
    # geom disk list
    # gmirror list

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

  5. Просим gmirror забыть обо всех дисках, которые сейчас неактивны в зеркале:
    # gmirror forget gm0
  6. gmirror обнаруживает, что da0 нет и забывает про него.
  7. Вставляем новый диск (желательно идентичный тому, с которым в паре он будет работать, вплоть до модели).
  8. Сканируем шину, чтобы система обнаружила новый подключённый диск:
    # : что имеем сейчас?
    # camcontrol devlist 
    
    # : сканируем
    # camcontrol rescan all
    
    # : что получилось?
    # camcontrol devlist
  9. Добавляем в массив новый da0:
    # gmirror insert gm0 da0
  10. gmirror обнаружит новый диск и начнёт синхронизацию данных. Смотрим состав массива:
    # gmirror list
  11. Не пугаемся, если нам кажется, что синхронизация идёт не в том направлении 🙂 Если заменялся da0, то теперь он в списке ПОСЛЕ da1, а не ДО, как был раньше.
  12. Испытываем счастье.

Почему mod_rewrite это вуду, и в каком на самом деле порядке выполняются правила RewriteRule

Сегодня мне пришлось составлять довольно хитрый редирект: если пользователь запросил /news/some-section/, то его нужно было перенаправить на просто /section/, но потом всё равно сделать внутренний редирект на /news/some-section/, так как сам движок новостей лежит именно там.

Я написал простое правило, обновил страничку… и получил от mod_rewrite по полной программе, потому что он ушёл в бесконечный цикл и не собирался из него возвращаться. Я открыл документацию и начал разбираться. Потом включил RewriteLog. Сначала я был озадачен. Потом стал откровенно злиться. А к тому моменту, когда задача была решена, я успел пережить такое количество разных чувств, что кроме равнодушного вздоха ничем больше не смог отметить достигнутый результат.

Оказывается, mod_rewrite называют вуду не потому, что он волшебный. А потому что для того, чтобы осознать, как он работает, нужно три раза прочитать по нему документацию. Вслух. С выражением! Потом найти все параграфы, начинающиеся со слова «Note» и пересказать их своими словами. Потом нужно найти все параграфы, в которых больше одного предложения и тоже пересказать их своими словами. Тоже вслух. И с выражением тоже.

Но и это ещё не всё. После этого нужно пойти по ссылке «detailed mod_rewrite documentation» и почитать там. А потом отправиться впитывать полезный опыт в гугл.

Так много слов выше написано оттого, что сам я только что всё это проделал и мне нужно высказаться. Вы, наверное, думаете — ну и где же обещанное «как на самом деле»? Подождите, сейчас расскажу.

По работе мне приходится составлять довольно простые правила, зато их довольно много. Приходится следить, чтобы они не пересекались друг с другом. Директив использую немного — RewriteCond, RewriteRule и флаги [L], [R], изредка [NC] и [QSA]. Без усложний с map’ами, chain’ами и subreq’ами.

Опыт программирования подсказывал мне (и некоторые другие программисты были со мной согласны), что если я сказал [R], то в этом месте должен начаться редирект. А если указано [L], то разбор правил должен прекратиться (совсем-совсем прекратиться). Ну и когда все правила пройдены, разбор прекращается, отдаётся скрипт, на который смотрит получившийся адрес.

Ага. Щаз.

Оказывается, всё работает несколько иначе.

[R] на самом деле не делает редирект. Он просто делает текущий адрес абсолютным (дописывает к нему http://хост/) и где-то себе запоминает, что потом нужно будет сделать внешний редирект. После этого разбор правил продолжается до конца .htaccess, и вот уже тут происходит перенаправление.

[L] на самом деле не останавливает полностью разбор правил.
Если разбор происходит в .htaccess (как это обычно бывает), то совершается переход в конец .htaccess, и вот тут уже… происходит перенаправление? Ну да, только в его результате мы с большой вероятностью опять попадаем на тот же .htaccess, и начинается проход по всем правилам заново (даже при внутреннем редиректе!). И этот цикл будет продолжаться до тех пор, пока не настанет момент, когда ни одно правило не сработает (или пока мы редиректом не выйдем из директории, в которой лежит наш .htaccess). Вот тогда уже сервер отдаст страничку.

Единственно предсказуемое поведение дают [R,L] вместе взятые. В этом случае отмечается, что должен произойти редирект, пропускаются все следующие правила и юзеру отдаётся перенаправление на указанную страницу.

Какой из этого вывод? Не надо везде подряд ставить [L]. Если, конечно, хотите понимать, в каком порядке обрабатываются правила. Если [L] нигде не указан, то проход осуществляется строго подряд. Правда, когда правила закончатся, и окажется, что какое-то из них поменяло ссылку, всё равно будет запущен проход по всем правилам ещё раз. Но это уже гораздо проще контролировать.

Например. Обычно ссылка после изменения не подходит под то же или под какое-либо другое правило. То есть, если вы перенаправляете /news в index.php/news:

RewriteRule ^news/$ index.php/news

то второй раз это правило уже не сработает.

Однако в моём случае нужно было запрос пользователя /news/section-name/ перенаправлять в /section-name/, а потом делать внутренний редирект обратно с /section-name/ на /news/section-name/, где находился обработчик новостей. То есть:

RewriteRule ^news/section-name/$ section-name/ [R=301,L]
RewriteRule ^section-name/ news/section-name/

Первый редирект внешний, второй внутренний. Если бы всё происходило в один проход, всё было бы нормально. Но так как проходы длятся пока срабатывает хотя бы одно правило, происходило следующее. Пользователь набрал /news/section-name/. Его внешним редиректом перенаправило на /section-name/. Потом сработало второе правило и произошёл внутренний редирект на news/section-name/. Потом mod_rewrite обнаружил, что ссылка изменилась, и инициировал повторную обработку запроса. Опять сработало первое правило, пользователь получил внешний редирект. Круг замкнулся.

Для того, чтобы разрешить внешний редирект только в том случае, если запрос /news/section-name/ прислан пользователем, а не сгенерирован RewriteRule, приходится проверять %{THE_REQUEST}:

RewriteCond %{THE_REQUEST} ^(GET|HEAD)\ /news/section-name/
RewriteRule ^news/(.*) /$1 [R=301,L]

Работает: после внешнего редиректа пользователь запрашивает /section-name/, этот запрос попадает в THE_REQUEST и для первого правила не выполняется RewriteCond.

За информацию спасибо следующим ссылкам:

* http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html
* http://www.askapache.com/htaccess/mod_rewrite-basic-examples.html
* http://httpd.apache.org/docs/2.2/rewrite/rewrite_tech.html
* http://www.google.com

Там написано, почему при обработке правил в .htaccess происходят повторные запросы с повторным проходом по всем правилам, и почему при указании директив редиректа в <VirtualHost> или в глобальном конфиге Apache не в разделе <Directory> проход происходит только один раз.

Передача ссылки во flash-баннер из html

Бывает так: есть flash-баннер, который должен ссылаться на какую-то странцу сайта. А адрес этой страницы неизвестен и станет известен только тогда, когда баннер собственно будет внедряться в код сайта. Или нужно иметь возможность менять ссылку во flash-баннере время от времени.

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

А хотелось бы иметь возможность изменить ссылку, на которую будет переводить баннер при клике, прямо из html-кода. Иными словами, передавать баннеру ссылку в виде параметра.

Как?

А вот как!

Подготовка flash-баннера

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

Первый кадр flash-баннера

В него нужно добавить такой код:

// _level0.bannerLink is passed via FlashVars
if (_level0.bannerLink == undefined) {
	// Default link will be used if no other link was specified via params
	var bannerLink:String = "http://www.sprinthost.ru";
} else {
	var bannerLink:String = _level0.bannerLink;
}

bannerLink — это параметр, который будет передаваться в ролик. В коде также предусмотрена ссылка по умолчанию.

Код кнопки

Сама кнопка для клика получает следующий код:

on(release) {
    getURL(bannerLink);
}

На этом с самим роликом всё. Осталось ещё добавить собственно передачу параметра в html-код.

Добавление параметров баннера в код html

В тэг <object>, который используется для внедрения баннера, нужно будет добавить код, который выделен жирным:

<object
  type="application/x-shockwave-flash"
  data="banner.swf"
  width="170" height="250">
    <param name="movie" value="banner.swf">
    <param name="wmode" value="transparent">
    <param name="allowScriptAccess" value="sameDomain" />
    <param name="FlashVars" value="bannerLink=http://www.google.com">
</object>

В данном примере ролику передаётся параметр, согласно которому он будет ссылаться на www.google.com.

Всё!

Поздравляю с достигнутым счастьем.