Обновить
44
0

Пользователь

Отправить сообщение
Скажите, чем вам нравится стандартный гугловский webapp? Ведь это же лоскутное одеяло — часть от Джанги, часть от WebOb. Нет удобного способа выставить куки (в т.ч. подписанные ключом), нет передачи сообщений между страницами. И вообще не хватает многих полезных штук, которые есть в других фреймворках из коробки.

Webapp позволяет быстро и с удобством разрабатывать разные приложения — от простеньких скриптов до полноценных сайтов. В нем практически ничего лишнего, но есть возможности для расширения функционала. Те же cookie ставятся легко и просто:
self.response.headers.add_header('Set-Cookie', 'a=b; path=/; expires=sometime; HttpOnly')

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

Плюс если вы уйдете от GAE, придется переписывать код. Я не предлагаю писать на Джанге (от нее там один огрызок), но flask, на мой взгляд, идеально подойдет.

GAE вообще слишком специфичен, чтобы разработанный под него код можно было сразу использовать. Свои API, свои ограничения — все это придется учитывать при переезде, либо намеренно не использовать возможности AE и держать неоптимальное, но готовое к переезду приложение.

Чем Flask не лоскутное одеяло? В чём преимущество Werkzeug + Jinja 2 относительно WebOb + Django? Только flask придется таскать из приложения в приложение, следить за обновлениями и надеяться, что разработчики не поломают совместимость с GAE. А webapp и django «всегда под рукой», оттестированы и готовы к работе, баги в них правятся без моего участия.

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

Один объект, два обработчика, полдюжины действий, ссылки на документацию и никаких хаков в коде. Да, функция __import__ относится к продвинутым функциям «не на каждый день», но используется способом, простейшим из возможных. В конце концов, код можно и просто скопипастить (он специально выложен одним куском) и наслаждаться ленивой загрузкой в ожидании просветления.
ну так это не имеет никакого отношения к webapp, а исключительно к директивам import в main.py
если там импортировать только wsgi то никаких тормоов не будет.

К webapp отношение самое непосредственное, в django, например, ленивая загрузка везде, где только возможно и для него подобные решения не нужны. WSGIApplication принимает вызываемые объекты в качестве аргументов, так что их все равно придется импортировать, с ленивой загрузкой или без нее.

под taskqueue вообще говоря можно и другой скрипт сделать, не main.

да и вообще, всё тяжёлое надо бы переносить на backend.

Приложение с множеством скриптов становится неудобным в разработке. Некоторые скрипты, вроде unit-тестов, можно спокойно вынести в отдельный файл. Но не те, что довольно тесно интегрированы с остальным приложением. И если основные скрипты лежат не в одной папке, то начнется путаница с путями и одни и те же модули придется импортировать разными путями.

А уж если используются дополнительные возможности, то будет веселье еще то: в каждый скрипт прописать use_library, свое middleware, свои фильтры для django, конфигурацию и следить, чтоб все везде было одинаково… Количество лишнего кода будет больше, чем 15 строк загрузчика.

С этим же загрузчиком подобных проблем не возникает. Кроме того, пользователю, зашедшему на главную страницу и нарвавшемуся на старт инстанса не придется ждать, пока загрузятся, например, нужные лишь на странице регистрации модули регулярок, капчи и отправки почты.
«Лишней нагрузки» в данном случае — небольшой перерасход памяти за счёт объектов-прослоек и этим спокойно можно пренебречь. Обработка одного списка при старте приложения тоже занимает совсем немного времени. Главная задача ленивого загрузчика — ускорить старт инстанса. И это ускорение достигается за счёт того, что модули импортируются только тогда, когда они нужны, а не все сразу при старте приложения.

«Размазывание» импорта на несколько запросов не ускоряет приложение чудесным образом, зато оно ускоряет выполнение самого медленного запроса. Маленькое приложение в 4 обработчика, приведённое в статье, не очень удачный пример. Зато приложение в полсотни модулей и полсотни обработчиков при грамотном разнесении обработчиков и использовании ленивой загрузки стартует в несколько раз быстрее. Плюс уменьшается потребление памяти за счёт того, что не держатся наготове всевозможные модули.
Именно, импорт множества модулей занимает довольно много времени. В обычном случае приложению на старте приходится импортировать все используемые где-либо модули. То есть даже если какой-то тяжелый модуль используется лишь в одном обработчике где-нибудь в taskqueue, пользователю всё равно придётся ждать, пока он подгрузится. Ленивый загрузчик позволяет не грузить его пока он не понадобится для обработки запроса.
Промазал с прошлым комментарием.
lambda-функция всё же лишняя, можно и проще:
application = WSGIApplication([(route[0], lazy_loader(route[1])) for route in [
  (r'/', 'handlers.MainPage'),
  (r'/search/', 'handlers.Search'),
  (r'/_ah/queue/sendmail', 'mail.SendMail'),
  (r'/_ah/queue/HeavyTask', 'tasks.HeavyTask'),
  ]], debug=os.environ.get('CURRENT_VERSION_ID').lower().startswith('dev'))
Это небольшие приятные мелочи питона (которых я пару месяцев назад тоже не знал) — фильтрование списков и lambda-функция, определённая в месте использования. О них очень хорошо написано в книге «Dive into python». В более привычном виде эта функция делает примерно следующее:
def process_list(li):
  result = []
  for route in li:
    result.append((route[0], lazy_loader(route[1])))
  return result
Я всё же немного о другом говорил — не о получении объектов по ключам, а о получении ключей по запросам. Это использование keys_only в model.all() или начало GqlQuery с "SELECT __key__ FROM model".

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

db.delete(sessions_model.all(keys_only=True).filter('last_activity <', datetime.datetime.now() - expiration_time).fetch(400))

При использовании keys_only=True функция fetch вернёт не список объектов, а список их ключей. Datastore отработает быстрее за счёт того, что не надо получать все объекты, а приложению надо будет переваривать меньший объём данных.
В некоторых случаях есть даже выгода — теперь можно спокойно грузить datastore задёшево. Если раньше 25К записей в день потребляли все 6,5 бесплатных часов CPU, то теперь большими пакетами можно насовать в разы больше. Соответственно, удалять большие объёмы теперь тоже дёшево и весело.

Но есть и большой перерасход для постоянно включенных экономных приложений. Теперь и мини-скрипт, занимающий менее 10Мб памяти и потребляющий минуты CPU в день будет считаться по времени работы инстансов по максимальной планке. То есть стоимость выполнения кода, оптимального для прошлого прайса, возрастает на порядки.

Плюс отвратные пограничные моменты. Надо отправить 101 email за день — плати $9 в месяц, надо 1.01 Гб места — плати $9 в месяц, и так далее. С blobstore вообще неведомая фигня — сейчас он работает только при включенном биллинге, а что будет потом не знает никто. Может быть, и за возможность закачки файлов придётся платить те же $9 в месяц.

Не паника, конечно. Но осадочек остаётся…
Если не секрет — чем занимается приложение? Что-то из разряда постоянно запущенных сервисов? Какие цифры могут быть, если «закрутить гайки» в настройках планировщика?
Размер одного отдельно взятого индекса целиком и полностью контролируется гуглом и у приложения нет возможности влиять на него. Здесь речь о том, что каждый индекс занимает дополнительное место.

Хранилище AE работает только по ключам, и индексы, по сути, являются костылём. Грубо говоря, индексы на одну модель — это отдельный объект в datastore со списком ключей объектов. Когда приложение делает запрос, сначала запрашивается по своему ключу объект индекса со списком ключей всех нужных записей, а потом все эти объекты берутся по ключам. Поэтому запросы только ключей выполняются заметно быстрее.

Например, если есть модель с 5-ю индексируемыми полями, то AE будет автоматически создавать 5 списков ключей, плюс индексы для сложных запросов, прописанные в index.yaml. И всё это хозяйство будет занимать место на диске. Причём этого места может требоваться очень дофига. У меня было не взлетевшее на старом прайсе небольшое приложение для сбора статистики — порядка 250К записей, в которых было около 12 индексируемых значений и не было сложных индексов. На 200Мб данных приходилось 800Мб индексов.

Оптимизация довольно простая — прописывать indexed=False где это возможно и не делать лишних сложных индексов. Могу показать на примере хабраподобных комментарев. Для RSS необходимо цеплять 20 последних комментариев топика, а для отображения на сайте нужно брать все в порядке возрастания. Можно вместо 2-х индексов (по возрастанию и убыванию времени комментария) пользоваться только индексом по убыванию — так как для отображения берутся все комментарии, можно взять их из datastore по убыванию, вознользовавшись «индексом для RSS» и просто развернуть массив в коде приложения.
Это хитрый план — хабрачитатели прочитают, ужаснутся и побегут учить английский… Переводы — не мой конёк, но надо же с чего-то начинать.
Скорее всего, инстанс при старте подгружается со всеми потрохами, и за счёт этого повышается скорость доступа к файлам приложения. Memcache не летает, конечно, но работает достаточно быстро — memcache.get отрабатывает за 5мс. Datastore, в первую очередь, должен хранить надёжно, к тому же есть ограничения масштабируемой архитектуры. Одну запись из него можно получить за 9мс. Более подробно цифры можно посмотреть здесь.
Оптимизация статики в App Engine это 5 строк в app.yaml:

default_expiration: "365d"
handlers:
- url: /(.*)/(.*)/(.*\.(css|js|gif|jpg|png|zip))
static_files: files/\2/\3
upload: files/(.*/.*\.(css|js|gif|jpg|png|zip))
Обрабатывает ссылки вида /v12345-anything/static/style.css

Преимущества:
  • Система статики App Engine более распределённая.
  • Ресурсы отдаются с адекватными заголовками и нормально кэшируются прокси-серверами: серверу не придётся даже отвечать на все запросы.
  • Не стартуют лишние инстансы, не потребляются лишние ресурсы приложения.

Совет по реализации: если Вы загружаете файлы вместе с приложением, то нет смысла класть их ещё и в memcache, и, тем более, в datastore. Скорость чтения из memcache и из локальных файлов почти одинаковая, я когда-то писал об этом.

Совет по применению: если Ваше приложение вынуждено генерировать и отсылать пользователям файлы, то стоит внимательно присмотреться к Files API. При отдаче файлов через свой скрипт необходимо укладываться в 30 сек, что может быть проблематично при узком канале у пользователя. А при отдаче через бэкенды их может понадобиться очень много при приличной посещаемости.
Стандартные решения для питона и для явы принимают файлы размером до 2Гб. Чем они не подходят?
А смысл? AppScale — исследовательский проект и не особо-то рассчитан на работу в production. Некоторые функции реализованы на базовом уровне, некоторые вообще отстутствуют.

Не лучше ли заточить приложение под App Engine и продолжать использовать на нём? На амазоне сильно дешевле не будет, а головной боли прибавится весьма прилично.
Это скорее обещание добавить информации для отладки приложений. Не так давно не показывали даже сколько инстансов запущено, а сейчас можно даже на графике посмотреть и сколько инстансов и сколько памяти они потребляют.

А ограничения были и раньше — надо же было как-то оптимизировать общее потребление и не позволять приложениям с утечками памяти влиять на производительность остальных. Особенность AE в том, что инстанс может взять довольно много памяти для объёмных вычислений. И даже если он возьмёт слишком много, то система даст ему закончить вычисления и остановит только после их завершения.
Если верить этому магазину (первый в гугле), то подходящие Вам NAKAI NE 4U 30W/833/E-27 стоят около 300р. Но за такие же деньги можно нарваться и низкокачественные qumo/zeon/русско-китайские поделки. Так что от выбора по маркировке и отверстиям, к сожалению не уйти.
Сравнение цен по CPU/Hr немного неправильное. Малый инстанс на AWS стоит $.085, что все же меньше, чем $0.1 от GAE. Малого инстанса хватает для большинства нужд. Малый инстанс обходится в $80 в месяц. А если купить Reserved Instance за $227, малый инстанс будет стоить $43 в месяц (это полезно делать, когда вы планируете работать с инстансом не меньше года — экономия присутствует).

Разве корректно сравнивать время работы виртуальной машины с процессорным временем?

Один computing unit примерно равен процессору 1.2ГГц, один час CPU в app engine также считается от этого ориентира. То есть, малый инстанс при 100% нагрузке за сутки выдаёт эквивалент 24-х часов процессорного времени app engine и потребляет $2,04. С учётом 6,5 бесплатных часов гугл за такой же объём вычислений возьмёт $1,75.

А если учесть то, что онлайн-сервисы работают на оборудовании с загрузкой много ниже 100%, то гугловские вычислительные мощности получаются существенно дешевле. Если уж нужен полноценный сервер для большого объёма вычислений за $80/мес, то на мой взгляд, Linode 2048 лучше подходит для таких задач.

Также не учтен лимит RAM (которого на GAE пока нет, но в будущем обещают).

Сейчас есть жёсткий лимит в 200Мб на инстанс, безболезненно же можно потреблять около 60Мб. Не поделитесь ссылкой на обещания?
Всё же некоторые дошли, добавил в пост немного про вентиляцию.
Обнаружил дополнительные вентиляционные отверстия на лампе. Видимо, именно благодаря им лампы хорошо работают в любом положении. Смотрите новые картинки в посте.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность