Cómo se manejan las solicitudes

ID de región

REGION_ID es un código abreviado que Google asigna en función de la región que seleccionas cuando creas la app. El código no corresponde a un país ni a una provincia, aunque algunos ID de región puedan parecer similares a los códigos de país y provincia que se suelen usar. En el caso de las apps creadas después de febrero de 2020, REGION_ID.r se incluye en las URL de App Engine. En el caso de las apps existentes creadas antes de esta fecha, el ID de región es opcional en la URL.

Obtén más información acerca de los ID de región.

En este documento, se describe cómo tu aplicación de App Engine recibe solicitudes y envía respuestas. Para obtener más detalles, consulta la referencia de encabezados de solicitud y respuestas.

Si la aplicación usa servicios, puedes direccionar solicitudes a un servicio específico o a una versión específica de ese servicio. Para obtener más información sobre cómo direccionar el servicio, consulta Cómo enrutar solicitudes.

Cómo controlar las solicitudes

La aplicación se encarga de iniciar un servidor web y controlar las solicitudes. Puedes usar cualquier marco de trabajo web que esté disponible para tu lenguaje de desarrollo.

App Engine ejecuta varias instancias de tu aplicación, y cada una tiene su propio servidor web para manejar solicitudes. Estas pueden enrutarse a cualquier instancia, por lo que las solicitudes consecutivas del mismo usuario no necesariamente se envían a la misma instancia. Una instancia puede manejar varias solicitudes al mismo tiempo. La cantidad de instancias se puede ajustar de forma automática a medida que cambia el tráfico. También puedes cambiar la cantidad de solicitudes simultáneas que puede controlar una instancia si configuras el elemento max_concurrent_requests en el archivo app.yaml.

Cuando App Engine recibe una solicitud web para la aplicación, llama a la secuencia de comandos del controlador que corresponde a la URL, como se describe en el archivo de configuración app.yaml de la aplicación. El entorno de ejecución de Python 2.7 admite el estándar WSGI y el estándar CGI para la retrocompatibilidad. Se prefiere WSGI, y algunas características de Python 2.7 no funcionan sin él. La configuración de los controladores de secuencia de comandos de la aplicación determina si una solicitud se maneja con WSGI o CGI.

La siguiente secuencia de comandos de Python responde a una solicitud con un encabezado HTTP y el mensaje Hello, World!.

import webapp2

class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('Hello, World!')

app = webapp2.WSGIApplication([
    ('/', MainPage),
], debug=True)

Para despachar varias solicitudes a cada servidor web en paralelo, agrega un threadsafe: true a su archivo app.yaml. Las solicitudes simultáneas no están disponibles si algún controlador de secuencia de comandos usa CGI.

Cuotas y límites

App Engine asigna recursos a tu aplicación de manera automática a medida que el tráfico aumenta. Sin embargo, esto se limita con las siguientes restricciones:

  • App Engine reserva la capacidad de ajuste de escala automático para aplicaciones con latencia baja a fin de que la aplicación responda a las solicitudes en menos de un segundo.

  • Las aplicaciones estrechamente vinculadas a la CPU también pueden incurrir en alguna latencia adicional para compartir recursos de manera eficaz con otras aplicaciones en el mismo servidor. Las solicitudes para archivos estáticos están exentas de estos límites de capacidad de latencia.

Cada solicitud que entra a la aplicación se tiene en cuenta para los límites de Solicitudes. Los datos enviados en respuesta a una solicitud se tienen en cuenta para el límite de Ancho de banda saliente (facturable).

Las solicitudes HTTP y las HTTPS (seguras) se tienen en cuenta para los límites de Solicitudes, Ancho de banda entrante (facturable) y Ancho de banda saliente (facturable). En la página Detalles de cuota de la consola también se informan las Solicitudes seguras, el Ancho de banda entrante seguro y el Ancho de banda saliente seguro como valores separados. Solo se tienen en cuenta las solicitudes HTTPS para estos valores. Para obtener más información, consulta la página de Cuotas.

Los siguientes límites se aplican específicamente al uso de los controladores de solicitudes:

Límite Cantidad
Tamaño de la solicitud 32 megabytes
Tamaño de la respuesta 32 megabytes
Tiempo de espera de la solicitud depende del tipo de escalamiento que use la app
cantidad total máxima de archivos (archivos estáticos y de la app) 10,000 en total
1,000 por directorio
tamaño máximo de un archivo de la aplicación 32 megabytes
tamaño máximo de un archivo estático 32 megabytes
tamaño total máximo de todos los archivos estáticos y de la aplicación el primer gigabyte es gratis
$0.026 por gigabyte por mes después del primer gigabyte
tiempo de espera de la solicitud pendiente 10 segundos
Tamaño máximo de un solo campo de encabezado de solicitud 8 kilobytes para los entornos de ejecución de segunda generación en el entorno estándar. Las solicitudes a estos entornos de ejecución con campos de encabezado que superen los 8 kilobytes mostrarán errores HTTP 400.

Límites de solicitudes

Todas las solicitudes HTTP/2 se convertirán en solicitudes HTTP/1.1 cuando se reenvíen al servidor de la aplicación.

Límites de la respuesta

  • Las respuestas dinámicas tienen un límite de 32 MB. Si un controlador de secuencia de comandos genera una respuesta que supera este límite, el servidor envía una respuesta vacía con un código de estado 500 “Error interno del servidor”. Esta limitación no se aplica a las respuestas que entregan datos desde Blobstore o Cloud Storage.

  • El límite del encabezado de respuesta es de 8 KB para los entornos de ejecución de segunda generación. Los encabezados de respuesta que excedan este límite mostrarán errores HTTP 502, con registros que muestran upstream sent too big header while reading response header from upstream.

Solicita encabezados

Una solicitud HTTP nueva incluye los encabezados HTTP que envía el cliente. Por motivos de seguridad, los proxies intermedios limpian o modifican algunos encabezados antes de que lleguen a la aplicación.

Para obtener más información, consulta la referencia Encabezados de solicitud.

Controla los tiempos de espera de las solicitudes

App Engine está optimizado para aplicaciones con solicitudes de corta duración, que son, por lo general, las que llevan unos cientos de milisegundos. Una app eficiente responde con rapidez a la mayoría de las solicitudes. Si la app no puede hacerlo, no escalará bien con la infraestructura de App Engine. Para garantizar este nivel de rendimiento, hay un tiempo de espera de solicitud máximo impuesto por el sistema al que cada app debe responder.

Si tu app supera este plazo, App Engine interrumpe el controlador de solicitudes. El entorno de ejecución de Python logra esto mediante la generación de una excepción DeadlineExceededError desde google.appengine.runtime. Si el controlador de solicitudes no detecta esta excepción, como ocurre con todas las excepciones no detectadas, el entorno de ejecución mostrará al cliente un error de servidor HTTP 500.

El controlador de solicitudes puede detectar este error para personalizar la respuesta. El entorno de ejecución le da al controlador de solicitudes un poco más de tiempo (menos de un segundo) después de generar la excepción para preparar una respuesta personalizada.

class TimerHandler(webapp2.RequestHandler):
    def get(self):
        from google.appengine.runtime import DeadlineExceededError

        try:
            time.sleep(70)
            self.response.write('Completed.')
        except DeadlineExceededError:
            self.response.clear()
            self.response.set_status(500)
            self.response.out.write(
                'The request did not complete in time.')

Si tu controlador no muestra una respuesta o genera una excepción antes del segundo plazo, el controlador se cierra y se muestra una respuesta de error predeterminada.

Respuestas

App Engine llama a la secuencia de comandos del controlador con una RequestRequest y espera a que la secuencia de comandos regrese. Todos los datos escritos en el flujo de salida estándar se envían como la respuesta HTTP.

Se aplican límites de tamaño a la respuesta que generas, y la respuesta se puede modificar antes de que regrese al cliente.

Para obtener más información, consulta la referencia Respuestas a solicitudes.

Respuestas a transmisión

App Engine no admite la transmisión de respuestas, donde los datos se envían en fragmentos graduales al cliente mientras se procesa una solicitud. Todos los datos de tu código se recopilan como se describió anteriormente y se envían como una respuesta HTTP simple.

Compresión de las respuestas

App Engine hace todo lo posible para entregar el contenido comprimido (en formato gzip) a los clientes que lo admiten. Para determinar si el contenido debe comprimirse, App Engine hace lo siguiente cuando recibe una solicitud:

  1. Visualiza los encabezados Accept-Encoding y User-Agent en la solicitud para confirmar si el cliente puede recibir respuestas comprimidas de manera confiable. Con este enfoque, se evitan algunos errores conocidos con contenido en formato gzip en navegadores populares.

  2. Visualiza el encabezado Content-Type que configuraste para el controlador de respuestas a fin de confirmar si la compresión del contenido es la opción correcta. Por lo general, la compresión es adecuada para los tipos de contenido basados en texto, no para los tipos de contenido binario.

Ten en cuenta lo siguiente:

  • Un cliente puede forzar la compresión de los tipos de contenido basado en texto mediante la configuración de los encabezados de solicitud Accept-Encoding y User-Agent en gzip.

  • Si una solicitud no especifica gzip en el encabezado Accept-Encoding, App Engine no comprimirá los datos de respuesta.

  • El frontend de Google almacena en caché las respuestas de los controladores de directorios y archivos estáticos de App Engine. Según diferentes factores, como qué tipo de datos de respuesta se almacenan primero en caché, qué encabezados Vary especificaste en la respuesta y qué encabezados se incluyen en la solicitud, un cliente podría solicitar datos comprimidos, pero recibir datos sin comprimir, y viceversa. Para obtener más información, consulta Almacena respuestas en caché.

Almacena respuestas en caché

El frontend de Google y, tal vez, el navegador del usuario y otros servidores proxy de almacenamiento en caché intermedia, almacenará en caché las respuestas de la app según lo indican los encabezados de almacenamiento en caché estándar que especifiques en la respuesta. Puedes especificar estos encabezados de respuesta a través del framework, directamente en el código o a través de los controladores de directorios y archivos estáticos de App Engine.

En el frontend de Google, la clave de caché es la URL completa de la solicitud.

Almacena el contenido estático en caché

Para garantizar que los clientes siempre reciban contenido estático actualizado en cuanto se publique, te recomendamos que entregues contenido estático de directorios con versión, como css/v1/styles.css. El frontend de Google no validará la caché (verifica si hay contenido actualizado) hasta que venza. Incluso después de que se venza la caché, no se actualizará hasta que cambie el contenido en la URL de la solicitud.

Los siguientes encabezados de respuesta que puedes establecer en app.yaml influyen en cómo y cuándo el frontend de Google almacena contenido en caché:

  • Cache-Control debe configurarse como public para que el frontend de Google almacene el contenido en caché. También puede almacenarse en caché mediante el frontend de Google, a menos que especifiques una directiva Cache-Control private o no-store. Si no configuras este encabezado en app.yaml, App Engine lo agrega de forma automática para todas las respuestas que administra un controlador de directorios o archivos estáticos. Para obtener más información, consulta Encabezados agregados o reemplazados.

  • Vary: Si quieres habilitar la caché a fin de que muestre respuestas diferentes para una URL según los encabezados que se envían en la solicitud, configura uno o más de los siguientes valores en el encabezado de respuesta Vary: Accept, Accept-Encoding, Origin o X-Origin.

    Debido a la posibilidad de cardinalidad alta, los datos no se almacenarán en caché para otros valores Vary.

    Por ejemplo:

    1. Especifica el siguiente encabezado de respuesta:

      Vary: Accept-Encoding

    2. La app recibe una solicitud que contiene el encabezado Accept-Encoding: gzip. App Engine muestra una respuesta comprimida, y el frontend de Google almacena en caché la versión comprimida de los datos de la respuesta. Todas las solicitudes posteriores para esta URL que contengan el encabezado Accept-Encoding: gzip recibirán los datos comprimidos de la caché hasta que se invalide la caché (debido a que el contenido cambia después de que se vence la caché).

    3. La app recibe una solicitud que no contiene el encabezado Accept-Encoding. App Engine muestra una respuesta sin comprimir, y el frontend de Google almacena en caché la versión sin comprimir de los datos de la respuesta. Todas las solicitudes posteriores para esta URL que no contengan el encabezado Accept-Encoding recibirán los datos comprimidos de la caché hasta que se invalide la caché.

    Si no especificas un encabezado de respuesta Vary, el frontend de Google crea una sola entrada de caché para la URL y la usará en todas las solicitudes, sin importar los encabezados de la solicitud. Por ejemplo:

    1. No debes especificar el encabezado de respuesta Vary: Accept-Encoding.
    2. Una solicitud contiene el encabezado Accept-Encoding: gzip y la versión comprimida de los datos de respuesta se almacenará en caché.
    3. Una segunda solicitud no contiene el encabezado Accept-Encoding: gzip. Sin embargo, debido a que la caché contiene una versión comprimida de los datos de la respuesta, la respuesta se comprimirá, a pesar de que el cliente solicitó datos sin comprimir.

Los encabezados de la solicitud también influyen en el almacenamiento en caché:

  • Si la solicitud contiene un encabezado Authorization, el frontend de Google no almacenará el contenido en caché.

Vencimiento de la caché

De forma predeterminada, los encabezados de almacenamiento en caché que los controladores de directorios y archivos estáticos de App Engine agregan a las respuestas indican a los clientes y a los proxies web, como el frontend de Google, que establezcan el vencimiento del almacenamiento en caché después de 10 minutos.

Después de que un archivo se transmite con un tiempo de vencimiento determinado, por lo general, no hay forma de borrarlo de las memorias caché del proxy web, incluso si el usuario borra su propia caché del navegador. Volver a implementar una versión nueva de la app no restablecerá ninguna caché. Por lo tanto, si alguna vez planeas modificar un archivo estático, este debe tener un tiempo de vencimiento corto (menos de una hora). En la mayoría de los casos, el tiempo de vencimiento predeterminado de 10 minutos es adecuado.

Puedes cambiar el vencimiento predeterminado de todos los controladores de directorio y archivos estáticos si especificas el elemento default_expiration en el archivo app.yaml. Si quieres configurar horas de vencimiento específicas para los controladores individuales, especifica el elemento expiration dentro del elemento controlador en el archivo app.yaml.

El valor que especifiques en la hora de vencimiento de los elementos se usará para establecer los encabezados de respuesta HTTP Cache-Control y Expires.

Almacenamiento en caché de las apps

El entorno de ejecución de Python almacena en caché módulos importados entre solicitudes en un único servidor web, de manera similar a como una aplicación Python independiente carga un módulo solo una vez, incluso si el módulo es importado por varios archivos. Dado que los controladores WSGI son módulos, se almacenan en caché entre solicitudes. Las secuencias de comandos del controlador CGI solo se almacenan en caché si proporcionan una rutina main() de lo contrario, se cargan para cada solicitud.

El almacenamiento en caché de la app proporciona un beneficio importante en el tiempo de respuesta. Recomendamos que todas las secuencias de comandos del controlador CGI utilicen una rutina main(), como se describe a continuación.

Las importaciones se almacenan en caché

Para mayor eficiencia, el servidor web mantiene los módulos importados en la memoria y no los vuelve a cargar ni los vuelve a evaluar en solicitudes posteriores a la misma aplicación en el mismo servidor. La mayoría de los módulos no inicializan ningún dato global o tienen otros efectos secundarios cuando se importan, por lo que el almacenamiento en caché no cambia el comportamiento de la aplicación.

Si la aplicación importa un módulo que depende del módulo que se evalúa para cada solicitud, la aplicación debe adaptarse a este comportamiento de almacenamiento en caché.

Almacenamiento en caché de controladores CGI

Puedes indicar a App Engine que almacene en caché la secuencia de comandos del controlador CGI, además de los módulos importados. Si la secuencia de comandos del controlador define una función llamada main(), la secuencia de comandos y su entorno global se almacenarán en caché como un módulo importado. La primera solicitud de la secuencia de comandos en un servidor web determinado evalúa la secuencia de comandos normalmente. Para solicitudes posteriores, App Engine llama a la función main() en el entorno almacenado en caché.

Para almacenar en caché una secuencia de comandos del controlador, App Engine debe poder llamar a main() sin argumentos. Si la secuencia de comandos del controlador no define una función main() o la función main() requiere argumentos (que no tienen valores predeterminados), App Engine carga y evalúa la secuencia de comandos completa para cada solicitud.

Mantener el código de Python analizado en la memoria ahorra tiempo y permite respuestas más rápidas. El almacenamiento en caché del entorno global también tiene otros usos potenciales:

  • Expresiones regulares compiladas. Todas las expresiones regulares se analizan y almacenan en un formulario compilado. Puedes almacenar expresiones regulares compiladas en variables globales, luego, usar el almacenamiento en caché de la aplicación para reutilizar los objetos compilados entre solicitudes.

  • Objetos GqlQuery. La string de consulta GQL se analiza cuando se crea el objeto GqlQuery. Reutilizar un objeto GqlQuery con vinculación de parámetros y el método bind() es más rápido que reconstruir el objeto cada vez. Puedes almacenar un objeto GqlQuery con la vinculación de parámetros para los valores en una variable global, luego, reutilizarlo vinculando nuevos valores de parámetros a cada solicitud.

  • Configuración y archivos de datos. Si tu aplicación carga y analiza los datos de configuración de un archivo, puede retener los datos analizados en la memoria para evitar tener que volver a cargar el archivo con cada solicitud.

La secuencia de comandos del controlador debe llamar a main() cuando se importa. App Engine espera que la importación de la secuencia de comandos llame a main(), por lo que App Engine no la llama cuando carga el controlador de solicitudes por primera vez en un servidor.

El almacenamiento en caché de apps con main() proporciona una mejora significativa en el tiempo de respuesta del controlador CGI. Lo recomendamos para todas las aplicaciones que usan CGI.

Registro

El servidor web de App Engine captura todo lo que la secuencia de comandos del controlador escribe en la transmisión de salida estándar para la respuesta a la solicitud web. También captura todo lo que la secuencia de comandos del controlador escribe en el flujo de error estándar y lo almacena como datos de registro. A cada solicitud se le asigna un request_id, un identificador único global según la hora de inicio de la solicitud. Los datos de registro de la aplicación se pueden ver en la consola mediante Cloud Logging.

El entorno de ejecución de Python de App Engine incluye una asistencia especial para el módulo de registro de la biblioteca estándar de Python que te ayuda a comprender los conceptos de registro, como los niveles de registro (“depuración”, “información”, “advertencia”, “error”, “crítico”).

import logging

import webapp2

class MainPage(webapp2.RequestHandler):
    def get(self):
        logging.debug('This is a debug message')
        logging.info('This is an info message')
        logging.warning('This is a warning message')
        logging.error('This is an error message')
        logging.critical('This is a critical message')

        try:
            raise ValueError('This is a sample value error.')
        except ValueError:
            logging.exception('A example exception log.')

        self.response.out.write('Logging example.')

app = webapp2.WSGIApplication([
    ('/', MainPage)
], debug=True)

Entorno

El entorno de ejecución establece automáticamente varias variables de entorno. puedes configurar más en app.yaml. De las variables configuradas automáticamente, algunas son especiales para App Engine, mientras que otras son parte de los estándares WSGI o CGI. El código de Python puede acceder a estas variables con el diccionario os.environ.

Las siguientes variables del entorno son específicas de App Engine:

  • CURRENT_VERSION_ID: la versión principal y secundaria de la aplicación actualmente en ejecución, como "X.Y". El número de la versión principal (“X”) se especifica en el archivo app.yaml de la app. El número de la versión secundaria ("Y") se configura automáticamente cuando cada versión de la aplicación se sube a App Engine. En el servidor web de desarrollador, la versión secundaria siempre es "1".

  • AUTH_DOMAIN: El dominio que se usa para autenticar usuarios con la API de usuarios. Las apps alojadas en appspot.com tienen un AUTH_DOMAIN de gmail.com y aceptan cualquier Cuenta de Google. Las aplicaciones alojadas en un dominio personalizado tienen un AUTH_DOMAIN igual al dominio personalizado.

  • INSTANCE_ID: contiene el ID de la instancia de frontend que maneja una solicitud. El ID es una string hexadecimal (por ejemplo, 00c61b117c7f7fd0ce9e1325a04b8f0df30deaaf). Un administrador que accedió puede usar el ID en una URL: https://INSTANCE_ID-dot-VERSION_ID-dot-SERVICE_ID-dot-PROJECT_ID.REGION_ID.r.appspot.com. La solicitud se enrutará a esa instancia de frontend específica. Si la instancia no puede manejar la solicitud, muestra un 503 de inmediato.

Las siguientes variables del entorno son parte de los estándares WSGI y CGI, con un comportamiento especial en App Engine:

  • SERVER_SOFTWARE: En el servidor web de desarrollador, este valor es "Desarrollo/X.Y", en el que "X.Y" es la versión del entorno de ejecución. Cuando se ejecuta en App Engine, este valor es "Google App Engine/X.Y.Z".

Las variables del entorno adicionales se configuran de acuerdo con el estándar WSGI o CGI. Para obtener más información sobre estas variables, consulta el estándar WSGI o el estándar CGI, según corresponda.

También puedes establecer variables de entorno en el archivo app.yaml:

env_variables:
  DJANGO_SETTINGS_MODULE: 'myapp.settings'

El siguiente controlador de solicitud webapp2 muestra todas las variables del entorno visibles para la aplicación en el navegador:

class PrintEnvironmentHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        for key, value in os.environ.iteritems():
            self.response.out.write(
                "{} = {}\n".format(key, value))

ID de solicitud

En el momento de la solicitud, puedes guardar el ID de solicitud, que es único para esa solicitud. El ID de solicitud se puede usar más adelante para buscar los registros de esa solicitud en Cloud Logging.

El siguiente código de muestra señala cómo obtener el ID de solicitud en el contexto de una solicitud:

class RequestIdHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        request_id = os.environ.get('REQUEST_LOG_ID')
        self.response.write(
            'REQUEST_LOG_ID={}'.format(request_id))

Fuerza conexiones HTTPS

Por motivos de seguridad, todas las aplicaciones deben incentivar al cliente a conectarse mediante https. A fin de indicarle al navegador que elija https en lugar de http para una página determinada o un dominio completo, configura el encabezado Strict-Transport-Security en tus respuestas. Por ejemplo:

Strict-Transport-Security: max-age=31536000; includeSubDomains
Si quieres configurar este encabezado para cualquier contenido estático que entregue la app, agrega el encabezado al controlador de directorios y archivos estáticos de la app.

Si quieres configurar este encabezado para las respuestas que se generan a partir de tu código, usa la biblioteca flask-talisman.

Administra el trabajo asíncrono en segundo plano

El trabajo en segundo plano es cualquier trabajo que tu app realice para una solicitud después de que entregaste la respuesta HTTP. Evita realizar tareas en segundo plano en la app y revisa el código para asegurarte de que todas las operaciones asíncronas finalicen antes de entregar la respuesta.

Para trabajos de larga duración, recomendamos usar Cloud Tasks. Con Cloud Tasks, las solicitudes HTTP son de larga duración y muestran una respuesta solo después de que finaliza cualquier trabajo asíncrono.