Mode de traitement des requêtes

ID de la région

Le REGION_ID est un code abrégé que Google attribue en fonction de la région que vous sélectionnez lors de la création de votre application. Le code ne correspond pas à un pays ou une province, même si certains ID de région peuvent ressembler aux codes de pays et de province couramment utilisés. Pour les applications créées après février 2020, REGION_ID.r est inclus dans les URL App Engine. Pour les applications existantes créées avant cette date, l'ID de région est facultatif dans l'URL.

En savoir plus sur les ID de région

Ce document décrit comment votre application App Engine reçoit des requêtes et envoie des réponses.

Pour en savoir plus, consultez la documentation de référence sur les en-têtes de requêtes et les réponses.

Si votre application utilise des services, vous pouvez adresser des requêtes à un service spécifique ou à une version particulière de ce service. Pour en savoir plus sur l'adressage des services, consultez la page Mode de routage des requêtes.

Traiter des requêtes

Votre application est responsable du démarrage d'un serveur Web et du traitement des requêtes. Vous pouvez utiliser n'importe quel framework Web disponible pour votre langage de développement.

App Engine exécute plusieurs instances de votre application, chacune d'elles disposant de son propre serveur Web pour le traitement des requêtes. Étant donné que chaque requête peut être acheminée vers n'importe quelle instance, les requêtes consécutives provenant d'un même utilisateur ne sont pas nécessairement envoyées à la même instance. Une instance peut traiter plusieurs requêtes simultanément. Le nombre d'instances peut être ajusté automatiquement en fonction de l'évolution du trafic. Vous pouvez également modifier le nombre de requêtes simultanées qu'une instance peut traiter en définissant l'élément max_concurrent_requests dans votre fichier app.yaml.

Lorsqu'App Engine reçoit une requête Web pour votre application, il appelle le script de gestionnaire correspondant à l'URL, comme décrit dans le fichier de configuration app.yaml de l'application. L'environnement d'exécution Python 2.7 est compatible avec les normes WSGI et CGI pour la rétrocompatibilité. Il est préférable d'utiliser WSGI, sinon certaines fonctionnalités de Python 2.7 ne fonctionneront pas. La configuration des gestionnaires de script de votre application détermine si une requête est gérée à l'aide de WSGI ou de CGI.

Le script Python suivant répond à une requête composée d'un en-tête HTTP et du message 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,
)

Pour répartir plusieurs requêtes en parallèle sur chaque serveur Web, marquez votre application comme threadsafe (à fil sécurisé) en ajoutant un élément threadsafe: true à votre fichier app.yaml. Les requêtes simultanées ne sont pas disponibles si un gestionnaire de script utilise CGI.

Quotas et limites

App Engine alloue automatiquement des ressources à votre application lorsque le trafic augmente. Toutefois, les restrictions suivantes s'appliquent :

  • App Engine réserve une capacité de scaling automatique pour les applications à faible latence, qui répondent aux requêtes en moins d'une seconde.

  • En outre, le temps de latence des applications fortement dépendantes des processeurs peut augmenter de manière à permettre le partage efficace des ressources avec les autres applications hébergées sur les mêmes serveurs. Aucune limite de latence n'est appliquée aux requêtes de fichiers statiques.

Chaque requête entrante vers l'application est prise en compte dans la limite des requêtes. Les données envoyées en réponse à une requête sont comptabilisées dans la limite de bande passante sortante (facturable).

Les requêtes HTTP et HTTPS (sécurisées) sont comptabilisées dans les limites de requêtes, de bande passante entrante (facturable) et de bande passante sortante (facturable). Dans la console Google Cloud, la page Détails des quotas affiche également les valeurs de Requêtes sécurisées, la Bande passante entrante sécurisée et la Bande passante sortante sécurisée de manière distincte à titre informatif. Seules les requêtes HTTPS sont comptabilisées dans ces valeurs. Pour en savoir plus, consultez la page Quotas.

Les limites ci-dessous s'appliquent spécifiquement à l'utilisation de gestionnaires de requêtes :

Limite Volume
Taille d'une requête 32 Mo
Taille d'une réponse 32 Mo
Délai avant expiration de la requête Dépend du type de scaling utilisé par votre application
Nombre total de fichiers, au maximum (fichiers d'application et fichiers statiques) 10 000 au total,
1 000 par répertoire
Taille maximale d'un fichier d'application 32 Mo
Taille maximale d'un fichier statique 32 Mo
Taille maximale du total des fichiers d'application et des fichiers statiques Le premier Go est gratuit
Le tarif est ensuite de 0,026 $ par Go et par mois
Délai avant expiration de la requête en attente 10 secondes
Taille maximale d'un seul champ d'en-tête de requête 8 kilo-octets pour les environnements d'exécution de deuxième génération dans l'environnement standard. Les requêtes adressées à ces environnements d'exécution avec des champs d'en-tête dépassant 8 kilo-octets renvoient des erreurs HTTP 400.

Limites de requêtes

Toutes les requêtes HTTP/2 sont converties en requêtes HTTP/1.1 lors de leur transmission au serveur d'applications.

Limites de réponse

  • Les réponses dynamiques sont limitées à 32 Mo. Si un gestionnaire de scripts génère une réponse supérieure à cette limite, le serveur renvoie une réponse vide avec un code d'état 500 indiquant une erreur interne du serveur. Cette limite ne s'applique pas aux réponses qui diffusent des données provenant de l'ancien service Blobstore ou de Cloud Storage.

  • La limite d'en-tête de réponse est de 8 ko pour les environnements d'exécution de deuxième génération. Les en-têtes de réponse qui dépassent cette limite renvoient des erreurs HTTP 502, avec des journaux affichant upstream sent too big header while reading response header from upstream.

En-têtes de requête

Une requête HTTP entrante inclut des en-têtes HTTP envoyés par le client. Pour des raisons de sécurité, certains en-têtes sont nettoyés ou modifiés par des serveurs proxy intermédiaires avant d'atteindre l'application.

Pour en savoir plus, consultez la documentation de référence sur les en-têtes de requête.

Gérer les délais d'expiration des requêtes

App Engine est optimisé pour les applications dont les requêtes sont de courte durée (quelques centaines de millisecondes en général). Pour être efficace, une application doit répondre rapidement à la majorité des requêtes. Si ce n'est pas le cas, elle n'est pas adaptée à l'infrastructure App Engine. Pour garantir ce niveau de performances, le système impose un délai d'expiration de la requête maximal pour chaque application.

Si votre application dépasse ce délai, App Engine interrompt le gestionnaire de requêtes. Pour ce faire, l'environnement d'exécution Python génère une exception DeadlineExceededError à partir de google.appengine.runtime. Si le gestionnaire de requêtes n'intercepte pas cette exception, l'environnement d'exécution renvoie une erreur de serveur HTTP 500 au client, comme pour toutes les exceptions non interceptées.

Le gestionnaire de requêtes peut intercepter cette erreur pour personnaliser la réponse. L'environnement d'exécution accorde au gestionnaire de requêtes un délai un peu plus long (moins d'une seconde) après la génération de l'exception pour lui laisser le temps de préparer une réponse personnalisée.

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 le gestionnaire n'a pas renvoyé de réponse ni généré d'exception à l'expiration du deuxième délai, il est arrêté. Un message d'erreur par défaut est alors renvoyé.

Réponses

App Engine appelle le script de gestionnaire avec un objet Request, puis attend qu'il renvoie un résultat. Toutes les données écrites dans le flux de sortie standard sont envoyées sous forme de réponse HTTP.

Des limites de taille s'appliquent à la réponse que vous générez et celle-ci peut être modifiée avant d'être renvoyée au client.

Pour en savoir plus, consultez la documentation de référence sur les réponses aux requêtes.

Réponses en flux continu

App Engine n'accepte pas les réponses en flux continu dans lesquelles les données sont envoyées au client par blocs incrémentiels pendant le traitement d'une requête. Toutes les données de votre code sont collectées comme indiqué ci-dessus et envoyées sous la forme d'une réponse HTTP unique.

Compression de la réponse

App Engine met tout en œuvre pour diffuser du contenu compressé avec gzip aux clients qui l'acceptent. Pour déterminer si le contenu doit être compressé, App Engine effectue les opérations suivantes lors de la réception d'une requête :

  1. Le service vérifie si le client peut recevoir des réponses compressées de manière fiable en consultant les en-têtes Accept-Encoding et User-Agent dans la requête. Cette approche permet d'éviter les bugs connus associés au contenu compressé avec gzip qui se produisent avec les navigateurs les plus courants.

  2. Vérifie si la compression du contenu est appropriée en affichant l'en-tête Content-Type que vous avez configuré pour le gestionnaire de réponses. En général, la compression est appropriée pour les types de contenus textuels et non pour les types de contenus binaires.

Veuillez noter les points suivants :

  • Un client peut forcer la compression des types de contenu textuel en définissant à la fois les en-têtes de requête Accept-Encoding et User-Agent sur gzip.

  • Si une requête ne spécifie pas gzip dans l'en-tête Accept-Encoding, App Engine ne compresse pas les données de réponse.

  • L'interface Google met en cache les réponses des gestionnaires de répertoires et de fichiers statiques App Engine. En fonction de divers facteurs, tels que le type des données de réponse mises en cache en premier, les en-têtes Vary que vous avez spécifiés dans la réponse ou les en-têtes inclus dans la requête, un client peut avoir demandé des données compressées, mais recevoir des données non compressées, et inversement. Pour en savoir plus, consultez la section Mettre en cache les réponses.

Mettre en cache les réponses

L'interface Google et éventuellement le navigateur de l'utilisateur ainsi que d'autres serveurs proxy de mise en cache intermédiaires mettent en cache les réponses de votre application, comme indiqué par les en-têtes de mise en cache standards que vous spécifiez dans la réponse. Vous pouvez spécifier ces en-têtes de réponse soit via votre framework, directement dans votre code, soit via les gestionnaires de répertoires et de fichiers statiques d'App Engine.

Dans l'interface Google, la clé du cache correspond à l'URL complète de la requête.

Mettre en cache du contenu statique

Pour vous assurer que les clients reçoivent toujours le contenu statique mis à jour dès sa publication, nous vous recommandons de diffuser le contenu statique à partir de répertoires avec versions gérées, tels que css/v1/styles.css. L'interface Google ne valide pas le cache (vous devez rechercher le contenu mis à jour) avant l'expiration du cache. Même après son expiration, le cache n'est pas mis à jour tant que le contenu de l'URL de la requête n'a pas été modifié.

Les en-têtes de réponse suivants, que vous pouvez définir dans app.yaml, modifient le moment et la méthode de mise en cache du contenu par l'interface Google.

  • Cache-Control doit être défini sur public pour que l'interface Google mette en cache le contenu. Il peut également être mis en cache par l'interface Google, sauf si vous spécifiez une instruction Cache-Control private ou no-store. Si vous ne définissez pas cet en-tête dans app.yaml, App Engine l'ajoute automatiquement pour toutes les réponses traitées par un gestionnaire de répertoires ou de fichiers statiques. Pour en savoir plus, consultez la section En-têtes ajoutés ou remplacés.

  • Vary : pour permettre au cache de renvoyer différentes réponses pour une URL en fonction des en-têtes envoyés dans la requête, définissez une ou plusieurs des valeurs suivantes dans l'en-tête de réponse Vary : Accept, Accept-Encoding, Origin, ou X-Origin.

    En raison du potentiel de cardinalité élevée, les données ne seront pas mises en cache pour d'autres valeurs Vary.

    Exemple :

    1. Vous spécifiez l'en-tête de réponse suivant :

      Vary: Accept-Encoding

    2. Votre application reçoit une requête contenant l'en-tête Accept-Encoding: gzip. App Engine renvoie une réponse compressée, et l'interface Google met en cache la version des données de réponse compressée avec gzip. Toutes les requêtes ultérieures pour cette URL contenant l'en-tête Accept-Encoding: gzip recevront les données du cache compressées avec gzip jusqu'à l'invalidation de celui-ci (en raison du changement de contenu après l'expiration du cache).

    3. Votre application reçoit une requête qui ne contient pas l'en-tête Accept-Encoding. App Engine renvoie une réponse non compressée, et l'interface Google met en cache la version non compressée des données de réponse. Toutes les requêtes ultérieures pour cette URL ne contenant pas l'en-tête Accept-Encoding recevront les données compressées du cache jusqu'à ce que le cache soit invalidé.

    Si vous ne spécifiez pas d'en-tête de réponse Vary, l'interface Google crée une seule entrée de cache pour l'URL et l'utilise pour toutes les requêtes, quels que soient les en-têtes de la requête. Exemple :

    1. Vous ne spécifiez pas l'en-tête de réponse Vary: Accept-Encoding.
    2. Une requête contient l'en-tête Accept-Encoding: gzip, et la version compressée avec gzip des données de réponse est mise en cache.
    3. Une deuxième requête ne contient pas l'en-tête Accept-Encoding: gzip. Toutefois, comme le cache contient une version compressée avec gzip des données de réponse, la réponse sera compressée avec gzip même si le client a demandé des données non compressées.

Les en-têtes de la requête influent également sur la mise en cache :

  • Si la requête contient un en-tête Authorization, le contenu ne sera pas mis en cache par l'interface Google.

Expiration du cache

Par défaut, les en-têtes de mise en cache que les gestionnaires de répertoires et de fichiers statiques App Engine ajoutent aux réponses indiquent aux clients et aux proxys Web, tels que l'interface Google, que le cache doit expirer au bout de 10 minutes.

Une fois un fichier transmis avec un délai d'expiration donné, il n'existe généralement aucun moyen de le supprimer des caches intermédiaires, même si l'utilisateur efface le cache de son propre navigateur. Le fait de déployer une nouvelle version de l'application ne permet pas de réinitialiser les caches. Par conséquent, si vous avez l'intention de modifier un fichier statique, il doit posséder un délai d'expiration court (moins d'une heure). Le délai d'expiration par défaut de 10 minutes convient dans la plupart des cas.

Vous pouvez modifier le délai d'expiration par défaut pour tous les gestionnaires de répertoires et de fichiers statiques en spécifiant l'élément default_expiration dans votre fichier app.yaml. Pour définir des délais d'expiration spécifiques pour des gestionnaires particuliers, spécifiez l'élément expiration dans l'élément gestionnaire de votre fichier app.yaml.

La valeur spécifiée dans le délai d'expiration des éléments sera utilisée pour définir les en-têtes de réponse HTTP Cache-Control et Expires.

Mise en cache des applications

L'environnement d'exécution Python met en cache les modules importés entre les requêtes effectuées sur un même serveur Web, de la même manière qu'une application Python autonome charge un module une seule fois même s'il est importé par plusieurs fichiers. Les gestionnaires WSGI étant des modules, ils sont mis en cache entre les requêtes. Les scripts de gestionnaire CGI ne sont mis en cache que s'ils fournissent une routine main(), sinon ils sont chargés pour chaque requête.

La mise en cache des applications offre un avantage significatif en terme de réduction du temps de réponse. Il est préférable que tous les scripts de gestionnaire CGI utilisent une routine main(), comme indiqué ci-dessous.

Les importations sont mises en cache

Pour plus d'efficacité, le serveur Web conserve en mémoire les modules importés, mais ne les recharge pas et ne les réévalue pas lors des requêtes suivantes adressées à la même application sur le même serveur. La plupart des modules n'initialisent aucune donnée globale et n'ont aucun autre effet secondaire lors de leur importation. Par conséquent, leur mise en cache ne modifie pas le comportement de l'application.

Si votre application importe un module en fonction de son évaluation lors de chaque requête, elle doit s'adapter à ce comportement de mise en cache.

Mettre en cache des gestionnaires CGI

Vous pouvez demander à App Engine de mettre en cache le script de gestionnaire CGI lui-même, en plus des modules importés. Si le script définit une fonction nommée main(), il est mis en cache avec son environnement global comme un module importé. La première requête exécutée pour le script sur un serveur Web donné évalue le script normalement. Pour les requêtes ultérieures, App Engine appelle la fonction main() dans l'environnement mis en cache.

Pour mettre en cache un script de gestionnaire, App Engine doit pouvoir appeler main() sans arguments. Si le script du gestionnaire ne définit pas une fonction main(), ou si la fonction main() requiert des arguments (qui ne possèdent pas de valeurs par défaut), App Engine charge et évalue le script entier pour chaque requête.

La conservation en mémoire du code Python analysé permet de gagner du temps et de réduire les temps de réponse. La mise en cache de l'environnement global offre d'autres possibilités d'utilisation :

  • Expressions régulières compilées : toutes les expressions régulières sont analysées et stockées sous une forme compilée. Vous pouvez stocker des expressions régulières compilées dans des variables globales, puis utiliser la mise en cache de l'application pour réutiliser les objets compilés entre les requêtes.

  • Objets GqlQuery : la chaîne de requête GQL est analysée lors de la création de l'objet GqlQuery. Il est plus rapide de réutiliser un objet GqlQuery avec une liaison de paramètre et la méthode bind() que de recréer l'objet à chaque fois. Vous pouvez stocker un objet GqlQuery avec une liaison de paramètre pour les valeurs présentes dans une variable globale, puis le réutiliser en liant de nouvelles valeurs de paramètre pour chaque requête.

  • Fichiers de configuration et de données : si votre application charge et analyse les données de configuration d'un fichier, elle peut conserver en mémoire les données analysées pour éviter d'avoir à recharger le fichier pour chaque requête.

Le script de gestionnaire doit appeler main() lorsqu'il est importé. Le comportement attendu par App Engine est que l'importation du script entraîne l'appel de main(). Par conséquent, App Engine ne l'appelle pas lors du premier chargement du gestionnaire de requêtes sur un serveur.

La mise en cache des applications avec main() offre une amélioration significative du temps de réponse de votre gestionnaire CGI. Nous recommandons de procéder de la sorte pour toutes les applications utilisant CGI.

Logging

Le serveur Web App Engine capture tous les éléments que le script du gestionnaire écrit dans le flux de sortie standard pour la réponse à la requête Web. Il capture également tous les éléments que le script du gestionnaire écrit dans le flux d'erreur standard et les stocke en tant que données de journal. Chaque requête se voit attribuer un request_id, un identifiant global unique basé sur l'heure de début de la requête. Les données de journal de votre application peuvent être affichées dans la console Google Cloud à l'aide de Cloud Logging.

L'environnement d'exécution Python d'App Engine offre une assistance particulière pour le module de journalisation de la bibliothèque standard Python afin d'expliciter des concepts tels que les niveaux de journalisation ("debug", "info", "warning", "error", "critical").

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)

Environnement

L'environnement d'exécution définit automatiquement plusieurs variables d'environnement. Vous pouvez en définir d'autres dans app.yaml. Certaines des variables définies automatiquement sont propres à App Engine, tandis que d'autres font partie des normes WSGI ou CGI. Le code Python peut accéder à ces variables à l'aide du dictionnaire os.environ.

Les variables d'environnement suivantes sont spécifiques à App Engine :

  • CURRENT_VERSION_ID : versions principale et secondaire de l'application en cours d'exécution (par exemple, "X.Y"). Le numéro de la version principale ("X") est spécifié dans le fichier app.yaml de l'application. Le numéro de la version secondaire ("Y") est défini automatiquement lorsque chaque version de l'application est importée dans App Engine. Sur le serveur de développement Web, la version secondaire correspond toujours à "1".

  • AUTH_DOMAIN : domaine utilisé pour l'authentification des utilisateurs à l'aide de l'API Users. Les applications hébergées sur appspot.com sont associées au domaine AUTH_DOMAIN gmail.com et acceptent n'importe quel compte Google. Le paramètre AUTH_DOMAIN des applications hébergées sur un domaine personnalisé correspond à ce dernier.

  • INSTANCE_ID : contient l'identifiant de l'instance frontend qui gère une requête. L'identifiant est une chaîne hexadécimale (par exemple, 00c61b117c7f7fd0ce9e1325a04b8f0df30deaaf). Un administrateur connecté peut utiliser cet identifiant dans une URL : https://INSTANCE_ID-dot-VERSION_ID-dot-SERVICE_ID-dot-PROJECT_ID.REGION_ID.r.appspot.com. La requête sera alors acheminée vers cette instance frontend spécifique. Si l'instance ne peut pas traiter la requête, elle renvoie immédiatement l'erreur 503.

Les variables d'environnement ci-dessous font partie des normes WSGI et CGI. Elles ont un comportement particulier dans App Engine :

  • SERVER_SOFTWARE : dans le serveur Web de développement, cette valeur est "Development/X.Y", où "X.Y" correspond à la version de l'environnement d'exécution. Lors de l'exécution sur App Engine, cette valeur est "Google App Engine/X.Y.Z".

Des variables d'environnement supplémentaires sont définies conformément à la norme WSGI ou CGI. Pour en savoir plus sur ces variables, reportez-vous à la norme WSGI ou à la norme CGI, le cas échéant.

Vous pouvez également définir des variables d'environnement dans le fichier app.yaml :

env_variables:
  DJANGO_SETTINGS_MODULE: 'myapp.settings'

Le gestionnaire de requêtes webapp2 ci-dessous affiche toutes les variables d'environnement visibles par l'application dans le navigateur :

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 requête

Au moment de la requête, vous pouvez enregistrer l'ID de requête, qui est unique à cette requête. Vous pourrez utiliser ultérieurement cet ID pour rechercher les journaux associés à la requête dans Cloud Logging.

L'exemple de code ci-dessous montre comment obtenir l'ID d'une requête dans le contexte de cette dernière :

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))

Forcer les connexions HTTPS

Pour des raisons de sécurité, toutes les applications doivent encourager les clients à se connecter via le protocole https. Pour indiquer au navigateur de privilégier https à http pour une page ou un domaine donné, définissez l'en-tête Strict-Transport-Security dans vos réponses. Exemple :

Strict-Transport-Security: max-age=31536000; includeSubDomains
Pour définir cet en-tête pour tout contenu statique diffusé par votre application, ajoutez-le aux fichiers statiques et gestionnaires de fichiers de votre application.

Pour définir cet en-tête pour les réponses générées à partir de votre code, utilisez la bibliothèque flask-talisman.

Gérer les tâches asynchrones en arrière-plan

Une tâche en arrière-plan désigne toute tâche effectuée par votre application pour une requête après la transmission d'une réponse HTTP. Évitez d'effectuer des tâches en arrière-plan dans votre application et examinez votre code pour vous assurer que toutes les opérations asynchrones sont terminées avant de transmettre votre réponse.

Pour les tâches de longue durée, nous vous recommandons d'utiliser Cloud Tasks. Avec Cloud Tasks, les requêtes HTTP ont une longue durée de vie et ne renvoient une réponse qu'une fois les tâches asynchrones terminées.