19 июня 2011 в 02:55

Использование Amazon Web Services на примере wikipaintings.org

Думаю, уважаемому сообществу будет интересно узнать о моем опыте разработки интернет проекта с использованием amazon web services.

Я не берусь утверждать, что весь проект идеален, однако я постараюсь описать основные решения, которые помогли сделать этот проект. Wikipedia.org, которая нас вдохновляла на работу, отдает 12 миллиардов страниц в месяц и поэтому мы старались с самого начала готовить код к росту популярности.

Что такое wikipaintings.org — www.wikipaintings.org/ru/About
Сейчас заканчивается первый этап разработки и основные задачи следующего этапа — привлечение волонтеров к наполнению сайта.

Если кому-то кажется, что картины – это скучно, оцените творчество Archimboldo — www.wikipaintings.org/ru/giuseppe-arcimboldo/spring-1573#supersized-artistPaintings-184903

Если же вам интересно наконец-то понять классификацию стилей живописи, добро пожаловать на www.wikipaintings.org/ru/paintings-by-style

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

Технологическая платформа

Данный проект выполнен с использованием ASP.NET MVC, LINQ2SQL, MS SQL Server 2008 R2 и JQuery. Не секрет, что AWS больше ориентирован на JAVA/PHP разработчиков, но я не чувствовал себя особо обделенным.
Предлагаю к рассмотрению мои советы разработчикам в порядке традиционных слоев веб приложений.

База данных

Мы начали с централизованной базы данных бизнес объектов – по нашим оценкам, достаточно мощный инстанс способен обработать нужное количество типовых запросов клиентов в ближайшем будущем (на данный момент это High Cpu Medium Instance). Для самых популярных HTTP запросов мы убедились, что выполняется ровно 1 SQL запрос, который возвращает все необходимые данные – именно базу данных сложнее всего отмасштабировать в данном сценарии.
Важно сказать решительное «нет» хранению файлов в базе данных. Несмотря на продвинутые возможности SQL Server 2008 это ложный путь. Не случайно EC2 расшифровывается как Elastic Computing Cloud – ваш сервер в первую очередь это просто процессор и его основное предназначение считать, а не хранить.
Файлов пользователей изначально будет слишком много, чтобы поместиться на любой диск – так что место им в S3 – бесконечном и очень надежном хранилище файлов. Возможно, оно в начале покажется медленным, но его скорость не меняется будет с ним работать 1 ваш процесс или 1000.
Кэшируйте результаты выполнения сложных запросов на уровне базы данных. У нас всевозможный поиск картин и художников инкапуслирован в одну хранимую процедуру. В качествуе ключа кэширования мы записываем все ее параметры и когда другой пользователь кликнет по тому же тэгу либо стилю у нас уже есть подготовленный список ID подходящих объектов в базе данных. Также это позволяет мгновенно отдать результат в случае использования пейджинга.
Упаковывайте подчиненные объекты вместе с основным. К примеру, изображение хранится как сериализованный XML в строке Картины и такой же XML в строке художника. Да, это не очень красиво, но каждый подзапрос это минимум 0.0005 секунды, и когда вы рисуете все painting на странице то это время нужно умножать на 200.
Также очень полезна статья «Открытие скрытых данных для оптимизации производительности приложений» http://msdn.microsoft.com/ru-ru/magazine/cc135978.aspx — я еженедельно проверяю, корректны ли индексы (уверен, что у многих она уже в закладках)

C# / Middle tier

Ведите учет времени рендеринга страницы и каждой серьезной операции. Недавно засветился профайлер от stackoverflow, но в целом можно быстро сделать что-то попроще самостоятельно. В BeginRequest – инициализация, EndRequest – проверяем, не рендерилась страница больше N секунд. Если рендерилась больше – пишем запись в лог ошибок. Расставляйте код, логирующий прошедшее время по приложению, когда нужно понять, сколько какой шаг занимает. Не помешает вывести такой лог в конце masterpage как html комментарий, чтобы наглядно видеть эффект от тюнинга производительности.
Сделайте методы контроллера «случайное действие» — это намного проще чем изучать selenium или что-то подобное, и позволит тестировать скорость работы разных фукнций приложения простыми тестами, такими как loadimpact.
Осторожно относитесь к вложенным циклам. Безобидный на вид цикл отрисовки, который в каждом шаге проходит по массиву с использованием Where(), выполняется десятую долю секунды. Такой же цикл, перед которым мы конвертируем массив в Dictionary – выполняется уже за сотую долю секунды.
Возвращайте несколько таблиц из одной SP даже если вы используете Linq2sql – это возможно и это неплохо работает. Я оцениваю накладные расходы по элементарной выборке объекта по PK в 0.005 секунды; если же такая операция идет циклом экономия растет многократно. К примеру, хранимая процедура возвращает не только картину, но и художника, который ее написал.
Вычитывайте данные пачками а не по одной записи. Так работает показ результатов поиска картин. После получения списка картин в C# строим список id художников и считываю их одним подзапросом в словарь, а потом назначаю считанные объекты соответствующим картинам.
Не используйте компонентный подход в его естественном виде, привитом ASP.NET, к компоновке страницы. Это предполагает минимум параметров у каждого компонента, что на практике ведет к тому, что каждый компонент вычитывает юзера, проверяет его права, читает свои частные настройки. Заведите контекст системы, действительный ровно на время запроса, куда по мере необходимости собирается вся необходимая информация и читается она ровно 1 раз.
Не полагайтесь на outputcache. Это круто, быстро, и это нужно использовать – он позволит снизить нагрузку на сервер, но это не панацея. К примеру, 18 мая 2010 было 19K page views, из них – 8500 РАЗНЫХ (по данным гугл аналитика). К тому же, outputcache собственный у каждого процесса – значит, затраты на память будут пропорционально количеству объектов в системе x количество серверов. Для главных страниц, у которых нет параметров, продолжительность кэширования может быть достаточно большой (15 минут), а для редких страниц мы ставим 30 секунд на случай публикации этой ссылке на хаббре. С другой стороны хорошо кэшируются ajax запросы – у них не так много параметров, они часто повторяются, и их результат, как правило, меньше чем у HTML страницы.
Выносите сложные части в отдельные проекты. Типичный сервер облака довольно слаб, но их может быть несколько. К примеру, мы вынесли генерацию thumbnails в отдельный веб сервис, что дало нам возможность разместить его на другом сервере и улучить скорость загруки картин при импорте.
Откладывайте то, что можно сделать не сейчас – Амазон предоставляет отличный, быстрый бесконечный и дешевый (наши затраты на этот сервис меньше 10 центов в месяц) инструмент очередь (см. Исходный код №2).
Используйте общую память. К примеру, на странице картины (http://www.wikipaintings.org/ru/giuseppe-arcimboldo/portrait-of-eve-1578) мы показываем другие картины этого художника, которые одинаковы для разных страниц. Сохранив минимально необходимую краткую информацию о картинах по каждому художнику в памяти мы уменьшили время генерации этой страницы в 2,5 раза. Можно, конечно, использовать Velocity но для нас стало проблемой отсутствие поддержки Windows 7, на которой мы разрабатываем. Мы написали собственный windows service с которым веб приложения общаются по remoting. По сути, это примерное повторение System.Web.Caching.Cache но оно поддерживает набор данных одинаковый для всех инстансов.

Клиентская часть

Проверяйте ваш вывод разными инструментами – такие утилиты как YSlow, Pagespeed, Firebug – ваши лучшие друзья.
Все что можно мы перекладываем на cloudfront. Очевидно — картины, thumbnails, но также мы записываем туда собранный в 2 файла CSS — один обычный, 1 – gzip. Важно именно расширение .gzip, так как .gz Safari не понимает. Данный прием продемонстрирован в примере кода №1.
Назначайте Expiration – по умолчанию S3 этого не делает (см. пример кода №1). Вы сэкономите время пользователя и деньги на трафике. Для этой операции и управления статическими файлами на s3 вообще рекомендую cloudberry.
Важным нюансом для повышения скорости загрузки страниц стало разнесение запросов по разным поддоменам. Протокол http не разрешает более 2х запросов к одному серверу одновременно; хоть браузеры и делают до 5ти запросов, загрузка страницы с 100+ картинками занимала больше 10 секунд. Мы разнесли картинки по поддоменам и теперь, при условии наличия хорошего канала, такая же страница загружается до 8 раз быстрее. Для этого создайте в cloudfront набор доменов (у нас – uploads0, uploads1, … uploads8) и выбирайте нужный псевдослучайным образом. Еще лучше зарегистрировать для этого отдельный домен чтобы при каждом запросе не передавались куки но мы сочли это паранойей.
Не загружайте больше информации, чем нужно чтобы показать страницу – все остальное можно загрузить потом по ajax. Чем быстрее грузится страница — тем больше лояльность пользователя. Главное, сделать симпатичную и адекватную анимацию при подгрузке данных.

Рекомендации по развертыванию

Мы используем 1 инстанс централизовано для базы данных, и набор micro инстансов которые занимаются рендерингом страниц под управлением load balancing (См. статью о настроке Windows Server 2008 R2 Core.
Сам Load balancing очень удобен в использовании — для работы с ним есть GUI. Желательно предусмотреть страницу состояния системы, которую амазон будет проверять с интервалом от 6 до 60 секунд (задается). В случае двух(можно больше) отрицательных результатов (таймаут либо код ошибки) инстанс объявляется «нездоровым» и запросы идут на другие сервера. После того как работоспособность восстанавливается он опять включается в работу.
1 load balancing в течении первого года предоставляется бесплатно.
Бакап базы делается ежедневно на отдельный EBS диск. Чтобы ни случилось с хост системой, либо самой windows, средставами панели управления можно будет отключить этот диск от одного виртуального сервера, подключить к новому, поднять бакап и продолжить работу. В качестве дальнейших шагов по повышению устойчивости системы к чрезвычайным происшествиям можно создавать snapshot этого диска автоматически.

Процесс разработки

Не последнюю роль сыграл и процесс разработки. Мы не опирались на фиксированную стоимость этапа проекта, просто каждую неделю выдавали новую стабильно работающую версию (хотя задержки, надо признать, были).
Во время итерации вся команда была сконцентрирована вокруг 3-4 основных задач, над которыми мы продолжали работать до тех пор, пока не были удовлетворены.
После запуска очередной версии реальная жизнь и реальные пользователи часто вносили коррективы, и 1-2 функции шли на очередную переделку.
Данный процесс ужасен, с точки зрения планирования? однако, он работает намного лучше стандартного аутсорсинга, где под каждую задачу заранее выделено количество времени, с точки зрения результата. Также он отлично влияет на мотивацию как с точки зрения того, что нет второсортных задач, так и поддерживает постоянный рефакторинг с тем, чтобы было комфортно вносить изменения в будущем.
Дайте клиенту признаться самому себе что важнее не сроки и бюджеты постройки неизвестно чего, а продукт который не пойдет в мусорную корзину и тогда, возможно, и вам удастся поработать с agile подходом.

Выводы


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

Полезные ссылки
1. Работа с S3
2. Работа с SQS
3. Настройка Windows Server 2008 R2 Core для Micro Intsance
4. SQL Server — Открытие скрытых данных для оптимизации производительности приложений
5. Мини профайлер для веб приложений от stackoverflow
Сергей Осипчук @osypchuk
карма
31,0
рейтинг 0,0
Самое читаемое Разработка

Комментарии (38)

  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Мы использовали linq2sql. Примерно 75% всех запросов делаются полностью через linq, остальные (которые нам показались относительно сложными) — через хранимые процедуры.
    • +2
      В топике автор написал, что использует LINQ2SQL
      • 0
        Ну вот, чуть-чуть опоздал с комментарием
  • 0
    Мы использовали linq2sql. Примерно 75% всех запросов делаются полностью через linq, остальные (которые нам показались относительно сложными) — через хранимые процедуры.
  • +4
    Сайт интересный, но стоит включить яваскрипт — сразу же становится в разы хуже. Вместо удобного списка маленьких картинок — какой-то кривой неудобный прокрутчик, который медленно-медленно их прокручивает. Полоска прокрутки фейковая — ОНА НЕ РАБОТАЕТ! Вместо открытия увеличенной фотки по клику — открывается уродливый промотрщик, в котором куча отвлекающих элементов, нижняя полоска грубо закрывает низ картины. И нельзя увеличить картинку больше размеров экрана (у меня экран широкий, но низкий, поэтому все картины становятся небольшими). Ужасно же.
    • 0
      >Полоска прокрутки фейковая — ОНА НЕ РАБОТАЕТ!
      На это есть причина — изначально мы ее сделали, но в момент протаскивания ползунка прокрутки на долю секунды видны все картины между начальным и конечным положением.
      Браузер начинает грузить сотню картинок и страница подвисает на десяток секунд.
      • +3
        Я тоже пытался воспользоваться этой прокруткой :-)

        Ваша отмазка, почему она не работает, не может являться оправданием — пользователь видит полосу прокрутки и будет пытаться воспользоваться ею. Не можете сделать ее рабочей — убирите.
        • –1
          Сейчас ее задача — информировать пользователя о том, сколько еще картин осталось в карусели.

          Думаю, какой-то выход чуть раньше или чуть позже найдется
        • 0
          Сделал, в следующем релизе будет работать )
  • 0
    Собственно, про «Использование Amazon Web Services» ничего конкретного и нет.
    • 0
      Спасибо за замечание. Добавил советы по развертыванию и примеры кода работы с S3 и SQS
  • +4
    У вас отличный проект, спасибо большое!
    Реквестую приложение для iPad в срочном порядке ;-)

    И если чуть серьезнее — как у вас дела с авторскими правами? Разве можно так свободно чужие картины показывать? Очень бы не хотелось, чтобы ваше начинание прикрыли копирасты.
    Недавно же вроде был скандал с Гуглом, который фотографировал в музеях. не знаю правда, чем там дело кончилось.
    • 0
      Естественно, в планах у нас есть приложение для мобильных устройств, но на данном этапе нужно проверить, будут ли сторонние люди помогать в наполнении системы контентом.

      Гугл арт живее всех живых, у них все хорошо :)
      По авторским правам ситуация действительно не самая простая. Законов и ньюансов у них очень много, но мы основываемся на двух принципах:
      1. Работы авторов, умерших более 75 лет назад (от штата к штату и от страны к стране эта цифра слегка меняется) принадлежать к public domain.
      2. http://en.wikipedia.org/wiki/Fair_use
      • 0
        Тогда еще раз спасибо большое.
        Успехов!
  • +2
    Немного не по теме:
    — очень интересный проект. Даже не подозревал о таком количестве стилей, хоть и заканчивал художественную школу;
    — оформление тоже приятное, чем-то напомнило 500px.com;
    — не нашел где можно посмотреть характеристики стиля.

    Спасибо.
    • +5
      Спасибо, записываю — нужна вики страница по каждому тэгу с описанием.
    • +2
      Ну и я бы не сказал, что ваш коментарий не по теме — я вижу, что всем интереснее сам проект, а не то, как он сделан, даже на сообществе программистов!

      Это лучшая оценка, на которую я мог рассчитывать
  • +1
    «Это произведение относится к общественному достоянию» — интересная формулировка. Но на русском языке по-моему надо писать «находится в общественном достоянии».
    • +1
      Спасибо за предложение, обязательно передам «куда надо»
  • 0
    image
  • 0
    Поправьте ссылки на Википедию, выглядят некрасиво. Пример: _http://www.wikipaintings.org/ru/sandro-botticelli

    Плюс русский язык не совсем русский
    # Участие
    about
    how to contribute
    blog
    обратная связь
    register
    • 0
      Спасибо за баг репорт
  • +1
    Кстати, я надеюсь, что коль уж в вашем проекте есть слова «вики» и «как внести свой вклад», то вы выпустите свою базу данных по свободной лицензии.
    • 0
      Разве я могу скачать базу данных википедии?

      Наверно, имелся ввиду исходный код? может быть
      • +1
        можете.
        dumps.wikimedia.org/
        а здесь есть руководство по развёртыванию зеркала
        • 0
          Спасибо — вижу, есть к чему стремиться.
  • +1
    Какие плагины JQuery Вы использовали?
    Понравилась реализация лайтбокса — очень информативен!
    • +1
      Спасибо. Стандартные jquery: autocomplete, slider, cookie
      jqModal (попапы)
      Галерея базируется на supersized но нам пришлось очень сильно ее переделывать, так как в опере эта библиотека регулярно делала ошибки в определении размера изображения.
      Карусель — sorgalla.com/projects/jcarousel/examples/static_controls.html
  • +1
    Наполнение оставляет желать лучшего. Вот пример «Иван Крамской. Родился: 8 июня 1837 г.; Ostrogozhsk, Russian Federation». Какая Российская Федерация в 19 веке?
    Или к примеру «Леон Бакст. Родился: 10 мая 1866 г.; Grodno, Belarus». Белоруссии не было в 19 веке. Это была провинция Российской империи". Все то же самое относится и к многим другим персонам.
    • 0
      По месту рождения вопрос хороший. На данный момент мы оттолкнулись от стандарта ISO, который игнорирует существование Российской империи www.iso.org/iso/english_country_names_and_code_elements#r..

      Видимо да, в какой-то момент придется переделывать
  • 0
    А тут еще и ссылка на ВП битая www.wikipaintings.org/ru/vladimir-borovikovsky
    И опять беда с местом рождения.
    • 0
      Все верно, ошибки в русских ссылках википедии уже зарепортил пользователь 1337.
  • 0
    Почему в русской версии пункт меню about а остальные на русском?
  • 0
    Ещё один твик на будущее ttp://www.google.ru/search?q=linq2sql+compiledquery
    • 0
      да да, у нас есть compiled query, однако, важнее уменьшать в разы количество запросов чем их компилировать, так что как-то упустил в статье
      • 0
        В вашем случае, конечно, это реально. У нас же это сделать нереально, а использование compiledquery увеличило скорость работы в 2-3(!) раза.
        • 0
          Неслабо. Возможно, я не в том месте пытался их применять.
          Спасибо за совет

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.