Рефакторинг отдельного сайта к Web-порталу.
Мне уже дважды пришлось переходить от своих отдельно взятых сайтов к порталам. Я нигде не видел в буквариках check-list такого перехода, не видел Best Practice на эту тему, не видел девелоперских ресурсов в инете, посвященных этой проблеме. Я даже спрашивал это на форумах, но никто не имеет четкого плана действий на эту тему. Поэтому я решил на этой страничке просуммировать некоторые стандартные необходимые действия - отчасти для себя для следующего развивающегося проекта, отчасти для всех остальных. Кроме того, от этой моей заметки начнут отталкиваться другие и в инете через некоторое время появится множество более удачных чек-листов по этому рефакторингу.
Сразу замечу, что портальная схема неудобна для девелоперов и надо отбиваться от этого желания заказчиков так долго, насколько это возможно. Но заказчики страшно любят порталы и... деваться некуда.
Итак, на входе есть один сайт - здесь и далее в примерах условно домен портала назван DEV.VM (это имя моего девелоперского кампутера). Понятно, что при выкладке на боевой продакшен-сервер портал получит постоянное имя с суффиксом например RU. Итак, на входе есть один сайт с именем DEV.VM, отдельные компоненты сайта распределены по классам и директориям DEV.VM/FOTO, DEV.VM/STORY, DEV.VM/NEWS, DEV.VM/USER и так далее. Требуется сделать портал (группу) из отдельных сайтов FOTO.DEv.VM, STORY.DEV.VM, NEWS.DEV.VM, USER.DEV.VM.
Перечень действий для реализации этого рефакторинга мы и рассмотрим на этой страничке:
- Итак, все начинается с DNS. Как на девелоперской машине, так и на боевом сервере. В DNS должны быть занесены все записи по вновь создаваемым адресам. Как видите, все имена смотрят на ту же машину. Если речь идет о девелоперском кампе, то достаточно не поднимать DNS, а просто прописать все в \WINDOWS\system32\drivers\etc. Обычно девелопер может ограничится WINDOWS\system32\drivers\etc ибо если сайт уже дорос до портала, то понятно что боевые сервера сопровождают уже совсем другие люди.
- Следующее действие - создание множества отдельных WEB-узлов, каждый из них слушает 80-й порт, но со своим host-header'ом.
- Следующий шаг - запуск службы ASP.NET State Service. Я предполагаю, что все узлы портала все еще может быть размещен на одном компьютере. Если все гораздо хуже и сайт уже никак невозможно захостить на обном компьютере - то надо переходить уже на хранение состояния в SQL-сервер и этот шаг уже конечно не нужен. Но мне не доводилось делать еще такие большие сайты, которые невозможно захостить на одном компьютере - поэтому о вариантах с кластерами я ничего ценного рассказать пока не могу.
- Далее следует настроить хранение состояния каждого узла в StateServer'е. Это можно сделать прямо в диалоге IIS, а можно просто занести к конфиг студии и IIS:
<system.web> <sessionState mode="StateServer"/>
- Чтобы не терялся залогиненный контекст, надо внести атрибут Domain для сквозной аутентификации и в студию и в IIS
<authentication mode="Forms"> <forms defaultUrl="Login.aspx" slidingExpiration="true" enableCrossAppRedirects="true" domain="dev.vm"/> </authentication>
- Я исхожу из того, что сайт делался в беспроектной схеме и без солушена. Но теперь придется создать общий Solution в VisualStudio. И включить в него все проекты отдельных Web-узлов. Если разработка ведется в Team Foundation server, то надо создать новые пути в ТИМе и определить Workspace для каждого отдельного узла. Вот как это выглядело в моем случае.
- Теперь надо указать стартовые странички в каждом новом созданном узле и в студии и IIS (или изготовить новые Default.aspx для каждого узла). Иначе вы получите стандартное сообщение - просмотр каталога запрещен. Надо выполнить всю прочую специальную настройку web-узлов, например внести FLV в mime-типы.
- Я рекомендую сдублировать в каждом узле собственную страничку обработки ошибок , а вот страничку логина, напротив - оставить единую (а именно это мы сделали в конфиге выше). Собственная страничка ошибок в каждом узле позволит надежнее получить сообщения об ошибках, особенно при взаимоблокировке узлов по доступу к общим ресурсам портала.
- Далее следует во всем солушене заменить все относительные ссылки (~/) на абсолютные (http://story.dev.vm, http://news.dev.vm, http://user.dev.vm и так далее). Это именно те ссылки, что прописаны в host-header'ах узлов IIS. Надо сообразить какая страничка теперь находится в каком именно узле. А это тысячи корректировок кода - и каждая из корректировок является источником багов.
- Далее следует выделить общий код из папок APP_CODE в отдельный проект библиотеки. Здесь все не так просто, как может показаться на первый взгляд. Как правило библиотеки работают с типизированными профилями. А это как раз тот самый компонент, который ну никак не хочет работать в библиотеке (даже если в библиотеку подсунуть сделанный вручную класс ProfileCommon). Кроме того, много меняется и в коде страниц и в коде библиотек при таком выделении. Например пространство имен My.Log превращается в My.Application.Log. Кроме того, надо сослаться теперь в каждом проекте на сборку перед именем класса. Те имя_сборки.имя_класса вместо просто имя_класса.
- Еще одна отдельная тема при выделении общего кода (кроме ProfileCommon) - это общие контролы. Это тоже непростая задача. В первом своем проекте, который проходил такой рефакторинг - я переписал эти контролы из ASP.NET на ASP и разместил их в отдельном узле, подключив их по <!--include. Таким образом я сохранил ОДИН экземпляр контролов на весь портал.
Во втором своем портале я просто создал в каждом проекте подкаталог COMMON и там сдублировал в каждом WEB-узле все общие контролы. Это проще, но утерялся весь смысл общих контролов. Теперь каждый из них вызывается один-два раза с тривиальнейшими параметрами. А в общем сайте каждый контрол вызывался страничек с десяти-двадцати. И каждый контрол был очень крученый и универсальный для работы в таких многих вариантах. Поддерживать такой универсальный код сложно, поэтому во втором своем портале я отказался от поддержки такого сложного и универсального кода и создал копии этих контролов для каждого отдельного Web-узла - имея ввиду в дальнейшем отдельный вектор модификации каждого контрола.
- Гораздо более очевидное решение с общими рисунками. Их можно вынести в отдельный узел другого домена. Это даже даст прирост производительности портала ибо реквесты в другой домен будут ходить без куков. А Web-узлов теперь все равно много. Теперь плюс-минус один - уже значения не имеет.
- Так же, как с рисунками, можно поступить и с хандлерами. Причем, если ваши хандлеры не используют IRequiresSessionState, а сделаны наследуемыми только от IHttpHandler - то по тем же соображениям выгоднее их вынести вообще в отдельный домен.
- Надо выделить общие автономные задачи, запускаемые в Global.asax в отдельный проект. Или просто оставить их в одном из проектов, как это сделал я.
- Еще одна тема - что делать с общими статическими классами приложения? Да... это вопрос. В общем случае я не знаю на него решения. Но мои общие статические классы работают например с криптографией. И они обладают АТОМАРНОСТЬЮ действий. Поэтому их можно оставить в каждом проекте. Хотя я не уверен в до конца в их корректной работе даже при наличии атомарности их действий. Этот пункт рефакторинга мне до конца неясен. Только стресс-тесты могут показать не получится ли так, что один узел перезапустится и заменит мне например ключи шифрования в Application("PP8"). А другой узел уверен, что там хранятся еще старые ключи шифрования.
Это одна тема. А вторая - непонятно как будет работать SYNCLOCK этого семафора при перезапуске другого узла, требующего доступа к этому эе семафору. На мой взгляд, тут под нагрузкой возможно все - от падения портала до зависания во взаимоблокировке. Но сейчас у меня довольно сжатые сроки этого рефакторинга и я просто вынужден сейчас отдать результат рефакторинга без этого тестирования. В этом портале не менее четверти миллиона посетителей в сутки - это и будет мой стресс-тест. Скорее всего портал будет самоблокироватся в клинче (по доступу к моим общим статическим классам портала) - и я уже примерно представляю себе код, который надо будет реализовать, чтобы не было самоблокировки. Придется выделять работу статических классов в отдельную WIN-службу, которая будет видна в трее иконкой и будет работать примерно по алгоритму микрософтовской ASP.NET State Service.
На 99% без такой службы не обойтись и я, возможно, позднее опубликую как строить такие службы для синхронизации доступа к общим статическим классам портала.
- Но вернемся к более примитивным темам этого рефакторинга. Как вообще запустить отладку портала? Ведь ссылки-то теперь абсолютные. И девелоперский сайт будет улетать по каждому линку на продакшен-сервер. Есть технология удаленного отладчика. Но ведь он подвесит боевой сервер. А как же мои четверть миллиона посетиттелей? Тоже будут ждать, пока я буду пошагово ходить по коду?
Я решил проблему отладки подменой линков. Я создал на девелоперском кампе клон продакшен конфигурации портала с доменом DEV.VM. Это было показано в первом-втором пункте. А непосредственно перед публикацией на боевой сервер я меняю абсолютные линки с девелоперских на боевые. Это позволяет отлаживать портал локально на девелоперском кампе - отлажтивать с абсолютными линками, которые никак не связаны с боевым сервером.
- Наверное каждому понятно, что девелоперский сервер уже никак не получится запустить в портале (по причине абсолютных линков). Поэтому первое что следует сделать для отлаки портала - отказаться от использования девелоперского сервера.
- Далее надо настроить IIS смотреть в те же директории, куда публикует портал студия. И вести отладку под IIS. Кстати, крупные сайты (а мелочь и не преобразуется в порталы) как правило используют вперемешку ASP и ASP.NET. Поэтому не надо забывать в IIS ставить на всех узла галку - Enable ASP.NET server side script debugging.
- Ну а DEBUG портала будет выглядеть как просто ATTACH к процессу IIS. Те сначала ставите на коде брейк, потом ATTACH на процесс W3WP.EXE. Это, конечно, немного отличается от отладки под девелоперским сервером - к такой отладке надо привыкнуть. Если бы такая отладка была бы достаточно прозрачной - то MS бы и не включала девелоперский сервер в состав Visual Studio.
- Для работы с порталом нужно еще немало навыков. Например надо взять за правило - после правки кода библиотеки сразу же прогонять Rebuild библиотеки. Иначе, даже несмотря на порядок компиляции проектов в солушене - в WEB-узлах останется старый код библиотек.
Как видите - даже по этому бегло составленному мною перечню - при рефакторинге сайта в портал все не настолько просто, как может показаться на первый взгляд. Трудоемкость работы с порталом, настройки, отладки, публикации, сопровождения - возрастает многократно. Девелоперский камп жужжит непрерывно и делает любую работу в портале в разы медленнее, чем в отдельном сайте. Обычные задачи при работе по портальной схеме включают дополнительные шаги, где легко допустить ошибку. Я не говорю уже о необходимости отдельных дополнительных приложений (например для предотвращения самоблокировки портала по доступу к общим статическим классам). И мы здесь не касались вообще еще многих и многих других тем работы в портальной схеме. Например темы взаимоблокировки портала на уровне SQL, из-за записи в одну и ту же базу сразу несколькими сайтами.
|