Избранное | Русский | Войти

Среда выполнения Python

App Engine выполняет код приложения Python с помощью предварительно загруженного интерпретатора в защищенной тестовой среде. Приложение выполняет веб-запросы и другие действия и получает ответы, взаимодействуя с этой средой.

Выбор среды выполнения Python

App Engine использует среду выполнения Python для кода приложения при использовании инструмента appcfg.py из SDK Python вместе с файлом конфигурации app.yaml. Чтобы выбрать среду выполнения Python, используйте следующие элементы конфигурации:

runtime: python
api_version: 1

Первый элемент runtime позволяет выбрать среду выполнения Python. На момент подготовки этой статьи App Engine поддерживает только один тип среды выполнения Python – python.

Второй элемент api_version позволяет выбрать версию среды выполнения Python, которую нужно использовать. На момент подготовки этой статьи App Engine поддерживает только одну версию среды выполнения Python – 1. Если коллективу App Engine потребуется выпустить изменения среды, не совместимые с существующим кодом, это будет сделано с новым номером версии. Приложение продолжит использовать выбранную версию до изменения параметра api_version и добавления приложения.

Дополнительную информацию о файлах app.yaml и appcfg.py можно найти в статьях Конфигурация приложения Python и Добавление приложения.

Запросы и CGI

При получении веб-запроса для приложения App Engine вызывает сценарий обработчика, соответствующий URL-адресу, согласно файлу конфигурации приложения app.yaml. App Engine использует стандарт CGI для передачи данных запроса обработчику и получения ответа.

App Engine использует несколько веб-серверов для работы приложения и автоматически подбирает количество, необходимое для надежной обработки запроса. Данный запрос может быть направлен любому серверу, а не только тому серверу, который обработал предыдущий запрос от того же пользователя.

Сервер определяет скрипт обработчика, который нужно выполнить при сравнении URL запроса и шаблонов URL в файле конфигурации приложения. Затем он выполняет обработчик в среде CGI с данными запроса. В соответствии с описанием в стандарте CGI сервер помещает данные запроса в переменные среды и стандартный поток ввода. Сценарий выполняет действия, соответствующие запросу, затем подготавливает ответ и помещает его в стандартный поток ввода.

Большинство приложений используют библиотеку для анализа запросов CGI и возврата ответов CGI, например, модуль cgi из стандартной библиотеки Python, или веб-инфраструктуру, представляющую возможность работы с протоколом CGI (например, webapp). Дополнительную информацию о переменных среды и формате данных потока ввода можно найти в документации по CGI.

При выполнении следующего сценария обработчика в браузере пользователя будет отображено сообщение. Сценарий помещает заголовок HTTP, определяющий тип сообщения, и содержание сообщения в стандартный поток вывода.

print "Content-Type: text/plain"
print ""
print "Hello, world!"

Ответы

App Engine получает все данные, записанные сценарием обработчика запросов в стандартный поток вывода, и ожидает окончание выполнения сценария. После завершения выполнения скрипта все данные вывода отправляются пользователю.

App Engine не поддерживает отправку данных в браузер пользователя до окончания работы обработчика. Некоторые веб-сервера использует технику потоковой передачи данных в браузер пользователя через определенный временной интервал в ответ на один запрос. App Engine это не поддерживает.

Если клиент отправляет заголовки HTTP с запросом, указывающим возможность получения сжатого содержания (gzip), App Engine автоматически сжимает данные запроса и присоединяет соответствующие заголовки ответа. Чтобы обеспечить надежное получение клиентом сжатых ответов, используются заголовки запроса Accept-Encoding и User-Agent. Чтобы принудительно сжать содержание, настраиваемым клиентам необходимо указать значение "gzip" в заголовках Accept-Encoding и User-Agent.

Таймер запросов

Обработчику запросов отведено ограниченное количество времени на создание и возврат ответа на запрос. Оно составляет примерно 30 секунд. Когда оно истекает, обработка запроса прерывается.

Среда выполнения Python прерывает обработчик запросов, вызывая исключение DeadlineExceededError из пакета google.appengine.runtime. Если обработчик запроса не перехватывает это исключение, среда выполнения вернет клиенту ошибку сервера HTTP 500 (как и со всеми неперехваченными исключениями).

Обработчик запроса может перехватить эту ошибку для настройки ответа. Среда выполнения дает обработчику запроса немного больше времени (меньше секунды) на подготовку другого ответа после создания исключения.

from google.appengine.runtime import DeadlineExceededError

class MainPage(webapp.RequestHandler):
  def get(self):
    try:
      # Do stuff...

    except DeadlineExceededError:
      self.response.clear()
      self.response.set_status(500)
      self.response.out.write("This operation could not be completed in time...")     

Если обработчик не вернул ответ или вызвал исключение до истечения второго срока, его работа прерывается и возвращается ответ с ошибкой по умолчанию.

Хотя выполнение запроса может занимать до 30 секунд, App Engine оптимизирован для приложений с короткими запросами, выполнение которых обычно занимает несколько сотен миллисекунд. Эффективное приложение должно быстро отвечать на большинство запросов. Приложение, не удовлетворяющее этому требованию, не удастся эффективно масштабировать на инфраструктуре App Engine.

Тестовая среда

Чтобы позволить App Engine распространять запросы для приложений на нескольких веб-серверах и предотвратить взаимодействие приложений, оно выполняется в ограниченной тестовой среде. В этой среде приложение может выполнять код, хранить и запрашивать данные из хранилища App Engine, использовать почту App Engine, службу получения данных по URL и службу пользователей, анализировать веб-запросы пользователя и подготавливать ответы.

Приложение App Engine не в состоянии:

  • Осуществлять запись в файловую систему. Для хранения постоянных данных приложения должны использовать хранилище данных App Engine. Можно осуществлять чтение из файловой системы. Кроме того, доступны все файлы, добавленные с помощью приложения.
  • Открывать канал или производить доступ к узлу напрямую. Приложение может использовать службу получения данных по URL App Engine для выполнения HTTP- и HTTPS-запросов на порты 80 и 443 соответственно.
  • Создавать подпроцесс или поток. Веб-запрос к приложению должен быть обработан одним процессом за несколько секунд. Чтобы избежать перегрузки веб-сервера, прерываются процессы, на выполнение которых требуется длительное время.
  • Выполнять другие виды системных вызовов.

Чистый Python

Среда выполнения Python использует Python версии 2.5.2.

Весь код для среды выполнения Python должен быть написан на чистом Python. Он не должен включать расширений C и другой код, который требует компиляции.

Среда включает стандартную библиотеку Python. Некоторые модули были отключены, поскольку App Engine не поддерживает их основные функции, такие как сетевые операции и запись в файловую систему. Кроме этого, доступен модуль os, но часть функций в нем отключена. При попытке импорта неподдерживаемого модуля или использовании неподдерживаемой функции возникает исключение.

Ряд модулей из стандартной библиотеки не изменен и не настроен для работы с App Engine. Например:

  • В качестве псевдонима для модуля cPickle используется модуль pickle. Функции, относящиеся к модулю cPickle, не поддерживаются.
  • Модуль marshal пуст. Импорт пройдет успешно, что нельзя сказать о его использовании.
  • Эти модули также являются пустыми: imp, ftplib, select, socket
  • Отключен модуль tempfile за исключением TemporaryFile, для которого используется псевдоним StringIO.
  • Рекомендуется использовать доступный модуль logging! См. ниже.

Помимо стандартной библиотеки Python и библиотек App Engine среда выполнения включает следующие сторонние библиотеки:

Чтобы добавить другие библиотеки чистого Python в свое приложение, добавьте соответствующий код в его каталог. Если в каталоге приложения создать символьную ссылку на каталог модуля, appcfg.py перейдет по ссылке и включит модуль в приложение.

Модуль Python включает путь к корневому каталогу приложения (каталогу, содержащему файл app.yaml). Модули, создаваемые в корневом каталоге приложения, доступны по пути от корня. Чтобы Python воспринимал подкаталоги как пакеты, создайте в них файлы __init__.py.

Кэширование приложений

Среда выполнения Python кэширует импортированные модули для выполнения различных запросов на одном веб-сервере аналогично тому, как отдельное приложение Python загружает модуль только один раз, даже если он импортируется несколькими файлами. Если сценарий обработчика содержит процедуру main(), то среда выполнения также кэширует и сценарий. В противном случае сценарий обработчика загружается для каждого запроса.

Кэширование приложений позволяет значительно сократить время ответа. Рекомендуется использовать процедуру main() во всех приложениях, как описано ниже.

Импорт кэшируется

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

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

В следующем примере показано кэширование импортированного модуля. Поскольку модуль mymodule импортируется только один раз для одного веб-сервера, глобальная переменная mymodule.counter инициализируется значением 0 только при обработке сервером первого запроса. Каждый последующий запрос будет использовать значение из предыдущего.

### mymodule.py
counter = 0
def increment():
  global counter
  counter += 1
  return counter


### myhandler.py
import mymodule

print "Content-Type: text/plain"
print ""
print "My number: " + str(mymodule.increment())

При этом будет выведено My number: #, где # – количество вызовов этого обработчика веб-сервером, обрабатывающим запрос.

Сценарии обработчиков тоже можно кэшировать

Можно указать App Engine, чтобы вместе с импортированными модулями он кэшировал сам сценарий обработчика. Если в сценарии обработчика определена функция main(), сценарий и его глобальная среда будут кэшированы так же, как импортированный модуль. При первом запросе анализ сценария на данном веб-сервере выполняется обычным образом. При последующих запросах App Engine вызывает функцию main() в кэшированной среде.

Чтобы кэшировать сценарий обработчика, App Engine должен иметь возможность вызвать функцию main() без аргументов. Если функция main() не определена в сценарии обработчика или функции main() необходимы аргументы (для которых не заданы значения по умолчанию), App Engine загружает и анализирует весь сценарий для каждого запроса.

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

  • Скомпилированные регулярные выражения. Все регулярные выражения анализируются и сохраняются в скомпилированной форме. Можно сохранять скомпилированные регулярные выражения в глобальных переменных, а затем применять кэширование приложений для повторного использования скомпилированных объектов в различных запросах.
  • Объекты GqlQuery. Строка запроса GQL анализируется при создании объекта GqlQuery. Быстрее повторно использовать объект GqlQuery с привязкой параметров и методом bind(), чем каждый раз заново создавать объект. Можно сохранить объект GqlQuery с привязкой параметров для значений в глобальной переменной, а затем повторно использовать его, выполняя привязку новых значений параметров для каждого запроса.
  • Файлы данных и конфигурации. Если приложение загружает из файла и анализирует данные о конфигурации, можно сохранить проанализированные данные в памяти, чтобы избежать необходимости повторно загружать этот файл при каждом запросе.

При импорте скрипт обработчика должен вызвать метод main(). App Engine ожидает, что при импорте скрипт вызовет метод main(), поэтому он не вызывает его при первой загрузке обработчика запросов на сервер.

В следующем примере выполняется то же, что и в предыдущем, с помощью кэширования глобальной среды сценария обработчика:

### myhandler.py

# A global variable, cached between requests on this web server.
counter = 0

def main():
  global counter
  counter += 1
  print "Content-Type: text/plain"
  print ""
  print "My number: " + str(counter)

if __name__ == "__main__":
  main()

Примечание. Будьте внимательны, чтобы не допустить утечки данных пользователей из одного запроса в другой. Избегайте использования глобальных переменных, если в кэшировании нет необходимости, и всегда выполняйте инициализацию данных конкретных запросов внутри процедуры main().

Кэширование приложений вместе с функцией main() значительно улучшает время ответа приложения. Рекомендуется использовать его для всех приложений.

Журналирование

Веб-сервер App Engine получает все данные, который сценарий обработчика записывает в стандартный поток вывода в ответ на веб-запрос. Он также получает все данные, который сценарий обработчика записывает в стандартный поток сообщений об ошибках и сохраняет в виде данных журнала. Данные журнала для приложения можно просмотреть и проанализировать с помощью Консоли администрирования или загрузить с помощью appcfg.py request_logs.

Среда выполнения Python App Engine содержит специальную поддержку для модуля logging из стандартной библиотеки Python, чтобы можно было понять концепции регистрации, такие как уровни сообщений ("debug", "info", "warning", "error", "critical").

import logging

from google.appengine.api import users
from google.appengine.ext import db

user = users.get_current_user()
if user:
  q = db.GqlQuery("SELECT * FROM UserPrefs WHERE user = :1", user)
  results = q.fetch(2)
  if len(results) > 1:
    logging.error("more than one UserPrefs object for user %s", str(user))
  if len(results) == 0:
    logging.debug("creating UserPrefs object for user %s", str(user))
    userprefs = UserPrefs(user=user)
    userprefs.put()
  else:
    userprefs = results[0]
else:
  logging.debug("creating dummy UserPrefs for anonymous user")

Среда

Среда выполнения содержит несколько переменных среды, полезных для приложения. Хотя некоторые из них входят в стандарт CGI, другие доступны только в App Engine. Код Python может получить доступ к этим переменным с помощью словаря os.environ.

Следующие переменные среды доступны только в App Engine:

  • APPLICATION_ID: идентификатор приложения, которое выполняется в текущий момент.
  • CURRENT_VERSION_ID: основная и дополнительная версии приложения, которое выполняется в текущий момент, в виде "X.Y". Основная версия ("X") указана в файле приложения app.yaml. Дополнительная версия задается автоматически при загрузке каждой версии приложения на App Engine. Она всегда равна "1" на веб-сервере разработки.
  • AUTH_DOMAIN: домен, используемый для аутентификации пользователей с помощью API пользователей. У приложений, размещенных на appspot.com, домен AUTH_DOMAIN на gmail.com. Они принимают любой аккаунт Google. У приложений, размещенных на других доменах с помощью Служб Google, AUTH_DOMAIN совпадает с соответствующим доменом.

Следующие переменные среды входят в стандарт CGI и обладают своеобразным поведением в App Engine:

  • SERVER_SOFTWARE: на веб-сервере разработки это значение равно "Development/X.Y", где "X.Y" – версия выполнения приложения.

Дополнительные переменные среды задаются в соответствии со стандартом CGI. Подробную информацию о этих переменных см. в стандарте CGI.

Совет. При выполнении следующего обработчика запросов каждая переменная среды, видимая приложению, отображается в браузере:

from google.appengine.ext import webapp
import os

class PrintEnvironmentHandler(webapp.RequestHandler):
  def get(self):
    for name in os.environ.keys():
      self.response.out.write("%s = %s<br />\n" % (name, os.environ[name]))

Квоты и ограничения

Каждый входящий в приложение запрос учитывается относительно квоты запросов.

Данные, получаемые как часть запроса, учитываются относительно квоты входящего трафика (оплачивается). Данные, отправляемые в ответ на запрос, учитываются относительно квоты исходящего трафика (оплачивается).

HTTP- и HTTPS-запросы учитываются относительно квот запросов, входящего трафика (оплачивается) и исходящего трафика (оплачивается). В информационных целях на странице "Сведения о квотах" Консоли администрирования также представлены отдельные значения квот безопасных запросов, безопасного входящего трафика и безопасного исходящего трафика. Для расчета этих значений учитываются только HTTPS-запросы.

Процессорное время, потраченное на выполнение обработчика запросов, учитывается относительно квоты процессорного времени (оплачивается).

Подробнее о квотах рассказано в разделе Квоты и в разделе Консоли администрирования "Сведения о квотах".

Помимо квот, к обработчикам запросов применяются следующие ограничения:

Ограничение Величина
Размер запроса 10 мегабайт
Размер ответа 10 мегабайт
Длительность запроса 30 секунд
Одновременные динамические запросы 30 *
Максимальное количество файлов приложения 1000
Максимальное количество статических файлов 1000
Максимальный размер файла приложения 10 мегабайт
Максимальный размер статического файла 10 мегабайт
Максимальный общий размер всех файлов приложения и статических файлов 150 мегабайт

* Приложение может одновременно обрабатывать порядка 30 динамических запросов. Это означает, что приложение, для которого среднее время выполнения запроса на стороне сервера составляет 75 миллисекунд, может без задержки обслужить до (1000 мс/с / 75 мс/запрос) * 30 = 400 запросов в секунду. При выполнении приложений, ограниченных возможностями процессора, может возникать дополнительная задержка в связи с необходимостью освободить ресурсы для других приложений, использующих эти же сервера. Это ограничение не влияет на запросы статических файлов.