Как стать автором
Обновить

10 вещей, которые вы (возможно) не знали про App Engine

Google App Engine *
Перевод
Автор оригинала: Nick Johnson, App Engine Team
Что может быть лучше, чем описание девяти классных фишек App Engine? Разумеется, описание десяти. Участвуя в обсуждениях в группах, мы заметили, что некоторые возможности App Engine часто остаются незамеченными, так что мы выбрали чуть меньше, чем одиннадцать интересных фактов, которые, возможно, просто позволят вам писать программы по другому. Но довольно слов, смотрим первую фишку:

1. Версии приложения — это строки, а не числа


Хотя в большинстве примеров в поле 'version' в app.yaml и в appengine-web.xml стоит число, тем не менее это всего лишь вопрос соглашения. В качестве версии приложения может использоваться любая строка, которую можно использовать в URL-ах. Например, вы можете назвать версии «live» и «dev», и они будут доступны по адресам «live.latest.yourapp.appspot.com» and «dev.latest.yourapp.appspot.com».

2. У вас может быть несколько одновременно работающих версий приложения


Как мы намекнули в пункте 1, App Engine разрешает деплоить несколько версий приложения и работать с ними параллельно. Все версии используют одни и те же хранилище данных и memcache, но исполняются в разных инстансах и у них разные URL-ы. Версия 'live' обслуживает адрес yourapp.appspot.com (и все другие домены, которые вы подключили), а все остальные версии приложений доступны по адресам вида version.latest.yourapp.appspot.com. Множественные версии удобно использовать для тестирования нового релиза в рабочем окружении, на реальных данных, прежде, чем вы его откроете для всех.

Менее известно, что разным версиям приложения даже не обязательно использовать один и тот же язык! Когда у вас одна версия приложения написана на Яве, а другая на Питоне — это довольно забавно.

3. Явовская среда исполнения поддерживает любые языки, которые компилируются в явовский байткод


Среда исполнения (runtime) называется явовской, но на самом деле ничто не запрещает вам написать AppEngine-овское приложение на любом языке, который компилируется в JVM-байткод. В общем-то уже есть люди, которые пишут AppEngine-овские приложения на JRuby, Groovy, Scala, Rhino (JavaScript-интерпретатор), Quercus-е (PHP интерпретатор/компилятор), и даже на Jython-е! На этой wiki-странице наше сообщество делится знанием о том, что заработало, а что нет.

4. Операторы 'IN' и '!=' генерируют несколько запросов к хранилищу данных


Операторы 'IN' и '!=' в питоновской среде исполнения на самом деле реализованы в SDK и 'под капотом' транслируются в несколько запросов.

Напимер, запрос «SELECT * FROM People WHERE name IN ('Bob', 'Jane')» транслируется в два запроса, эквивалентных выполнению «SELECT * FROM People WHERE name = 'Bob'» и «SELECT * FROM People WHERE name = 'Jane'» и объединению результатов. Использование нескольких операторов одновременно ещё больше увеличивает количество необходимых запросов, таким образом запрос «SELECT * FROM People WHERE name IN ('Bob', 'Jane') AND age != 25» суммарно выполняет четыре реальных запроса, по всем вариантам условий (возраст меньше или больше 25-и лет, и имя 'Bob' или 'Jane'), а потом объединяет их в один результат.

Как следствие, лучше стараться избегать использования нескольких таких операторов в одном запросе. Например, если вы используете запрос с неравенством, и ожидаете, что критериям будет соответствовать лишь небольшое количество записей (скажем, в вышеприведённом примере вы знаете, что очень мало людей имеют возраст ровно 25 лет), то может быть более эффективно выполнить запрос без неравенства и самому перебрать результаты и отбросить те записи, которые не подходят.

5. Для большей эффективности можно объединять операции вставки, запроса и удаления в пакеты


Каждый раз, когда вы выполняете запрос к хранилищу данных, такой как выборка или операция get(), ваш приложение посылает этот запрос хранилищу, там запрос обрабатывается, и обратно отправляется ответ. Этот цикл запрос-ответ занимает время, и если вы делаете много операций одну за другой, то к тому времени, которое пользователь ждёт результата, может добавиться существенный довесок.

К счастью, есть простой способ уменьшить количество мотаний туда-сюда: пакетные операции. Каждая из функций db.put(), db.get() и db.delete() помимо своего обычного одиночного вызова может принимать на вход список. Если передать им список, то они выполнят операции со всеми элементами в списке за одно обращение к хранилищу данных, причём параллельно, сохраняя вам кучу времени. К примеру, посмотрите на это типичное решение:

for entity in MyModel.all().filter("color =",
    old_favorite).fetch(100):
  entity.color = new_favorite
  entity.put()

Обновление в таком стиле требует обращения к хранилищу для выборки данных плюс по одному дополнительному обращению на каждое обновление объекта — получая в итоге 101 обращение к хранилищу! Для сравнения, посмотрите на этот пример:

updated = []
for entity in MyModel.all().filter("color =",
    old_favorite).fetch(100):
  entity.color = new_favorite
  updated.append(entity)
db.put(updated)

Добавив две строчки мы уменьшили количество обращений к хранилищу со 101-го до 2-х!

6. Производительность хранилища данных не зависит от того, сколько объектов в нём хранится


Многие спрашивают, как хранилище поведёт себя, если сохранить 100,000, или миллион, или десять миллионов объектов. Один из основных плюсов хранилища — это то, что его производительность совершенно не зависит от количество объектов, хранимых вашим приложением. Более того, фактически все объекты всех приложений в App Engine хранятся в одной таблице BigTable! Далее, что касается запросов — все запросы, которые могут быть выполнены «нативно» (исключая те, в которых используются операторы 'IN' и '!=' — см. выше), имеют одинаковую стоимость выполнения: стоимость исполнения запроса пропорциональна количеству записей, которые он возвращает.

7. Время, необходимое для создания индекса не полностью зависит от его размера


При добавлении нового индекса в приложение на App Engine иногда случается так, что он создаётся довольно долго. Спрашивая о причинах этого, многие упоминают количество данных, сравнивая его с затраченным временем. Однако дело в том, что запросы на создание новых индексов организуются в очередь и обрабатываются централизованной системой, которая создаёт индексы для всех AppEngine-овских приложений. В часы пик перед вашим запросом на создание индекса могут оказаться другие, и мы сможем начать создавать ваш индекс чуть позже.

8. Количество сохранённых данных ('Stored Data') расчитывается один раз в день


Раз в день мы запускаем для вашего приложения задачу пересчёта значения 'Stored Data'. При этом используется значение фактического использования хранилища данных в этот момент времени. В промежутках мы обновляем картинку, примерно учитывая то, как вы используете хранилище, так что все изменения в использовании вы можете увидеть довольно быстро. Отсюда видно, почему многие наблюдали такой эффект, когда после удаления большого количества объектов их хранилище ещё некоторое время оставалось «заполненным». При оплате, разумеется, используются только подтверждённые значения.

9. Порядок, в котором перечислены хендлеры (handlers) в файлах app.yaml, web.xml и appengine-web.xml имеет значение


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

handlers:
- url: /.*
  script: request.py

- url: /remote_api
  script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py
  login: admin

На первый взгляд здесь всё в порядке, но так как хендлеры обрабатываются по порядку, хендлер request.py встречается первым и все запросы — даже к remote_api — идут через request.py. Так как request.py ничего не знает про remote_api, то он возвращает ошибку 404 Not Found. Решение простое — убедиться, что хендлер «ловить всё» стоит в самом конце.

Это же относится и к явовской среде исполнения, но ещё с одним ограничением: все статические хендлеры из appengine-web.xml обрабатываются раньше, чем любые динамические хендлеры из web.xml.

10. Не нужно писать GQL запросы руками


Есть один очень частый анти-паттерн, выглядящий примерно так:

q = db.GqlQuery("SELECT * FROM People "
    "WHERE first_name = '" + first_name 
    + "' AND last_name = '" + last_name + "'")

Помимо того, что это делает ваш код уязвимым к инъекциям, вам придётся разбираться с экранированием символов (а что, если у пользователя апостроф в имени?) и, потенциально, с кодировками. К счастью, GqlQuery умеет делать подстановку параметров, стандартный способ избежать явного сложения строк. С подстановкой параметров вышеприведённый запрос можно переписать так:

q = db.GqlQuery("SELECT * FROM People "
    "WHERE first_name = :1 "
    "AND last_name = :2", first_name, last_name)

GqlQuery кроме нумерованных параметров также поддерживает именованные и передачу пар имя-значение в качестве аргумента:

q = db.GqlQuery("SELECT * FROM People "
    "WHERE first_name = :first_name "
    "AND last_name = :last_name", 
    first_name=first_name, last_name=last_name)

Так не только получается более чистый код, но ещё и можно использовать кое-какие интересные оптимизации. Если надо выполнить один и тот же запрос много раз с разными значениями, то можно использовать GqlQuery-вский .bind(), чтобы 'подключить' значения параметров для каждого запроса. Это быстрее, чем создавать каждый раз новый запрос, потому что парсить запрос придётся только один раз:

q = db.GqlQuery("SELECT * FROM People "
    "WHERE first_name = :first_name "
    "AND last_name = :last_name")
for first, last in people:
  q.bind(first, last)
  person = q.get()
  print person


Автор Nick Johnson, App Engine Team

Java is a trademark or registered trademark of Sun Microsystems, Inc. in the United States and other countries.

PS. Странно, я жал только на «в черновики» и «предпросмотр», на «опубликовать» с гарантией не жал — а топик всё равно опубликовался (естественно, недоделанный). Прошу прощения у всех, кого это смутило — и не надо так нервничать :-)
Теги:
Хабы:
Всего голосов 41: ↑38 и ↓3 +35
Просмотры 1.6K
Комментарии Комментарии 11