Como as solicitações são tratadas

ID da região

O REGION_ID é um código abreviado que o Google atribui com base na região que você selecionou ao criar o aplicativo. O código não corresponde a um país ou estado, ainda que alguns IDs de região sejam semelhantes aos códigos de país e estado geralmente usados. Para apps criados após fevereiro de 2020, o REGION_ID.r está incluído nos URLs do App Engine. Para apps existentes criados antes dessa data, o ID da região é opcional no URL.

Saiba mais sobre IDs de região.

Este documento descreve como um aplicativo do App Engine recebe solicitações e envia respostas.

Para mais detalhes, consulte a referência sobre respostas e cabeçalhos de solicitação.

Caso seu aplicativo use serviços, é possível endereçar solicitações para um serviço específico ou uma determinada versão desse serviço. Para mais informações sobre a capacidade de endereçamento do serviço, consulte Como as solicitações são encaminhadas.

Como processar solicitações

O aplicativo é responsável por iniciar um servidor da Web e processar as solicitações. É possível usar qualquer framework da Web disponível na linguagem de programação adotada.

Quando o App Engine recebe uma solicitação da Web para seu aplicativo, ele invoca o servlet que corresponde ao URL, conforme descrito no arquivo web.xml no diretório WEB-INF/ do aplicativo. Ele é compatível com as especificações da API Java Servlet 2.5 ou 3.1 (em inglês), para fornecer os dados da solicitação ao servlet e aceitar os dados de resposta.

Várias instâncias do aplicativo são executadas no App Engine, e cada uma tem um servidor da Web próprio para processar as solicitações. Como cada solicitação pode ser encaminhada para qualquer instância, solicitações consecutivas do mesmo usuário não são necessariamente enviadas para a mesma instância. O número de instâncias pode ser ajustado automaticamente, à medida que o tráfego muda.

Por padrão, cada servidor da Web processa apenas uma solicitação por vez. Para expedir várias solicitações para cada servidor da Web paralelamente, marque seu aplicativo como threadsafe adicionando um elemento <threadsafe>true</threadsafe> ao arquivo appengine-web.xml.

O exemplo de classe de servlet a seguir exibe uma mensagem simples no navegador do usuário.

// With @WebServlet annotation the webapp/WEB-INF/web.xml is no longer required.
@WebServlet(name = "requests", description = "Requests: Trivial request", urlPatterns = "/requests")
public class RequestsServlet extends HttpServlet {

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    resp.setContentType("text/plain");
    resp.getWriter().println("Hello, world");
  }
}

Cotas e limites

No App Engine, os recursos são alocados automaticamente para o aplicativo à medida que o tráfego aumenta. No entanto, isso é limitado pelas seguintes restrições:

  • O App Engine reserva a capacidade de escalonamento automático para aplicativos com baixa latência, em que a resposta a uma solicitação ocorre em menos de um segundo.

  • Aplicativos que fazem muito uso da CPU podem gerar mais latência, a fim de compartilhar recursos de maneira eficiente com outros aplicativos nos mesmos servidores. Solicitações de arquivos estáticos estão isentas dos limites de latência.

Cada solicitação recebida para o aplicativo é contabilizada no limite de Solicitações. Os dados enviados em resposta a uma solicitação são contabilizados no limite de Largura de banda de saída (faturável).

Tanto as solicitações HTTP quanto as HTTPS (seguras) são contabilizadas nos limites de Solicitações, Largura de banda de entrada (faturável) e Largura de banda de saída (faturável). A página "Detalhes da cota" do console do Google Cloud também exibe Solicitações seguras, largura de banda de entrada segura e largura de banda de saída segura como valores separados para fins informativos. Apenas solicitações HTTPS são contabilizadas nesses valores. Para mais informações, consulte a página Cotas.

Os limites a seguir se aplicam especificamente ao uso de gerenciadores de solicitações:

Limite Valor
Tamanho da solicitação 32 megabytes
Tamanho da resposta 32 megabytes
Tempo limite da solicitação Depende do tipo de escalonamento usado pelo aplicativo
Número máximo total de arquivos (arquivos de aplicativos e arquivos estáticos) 10.000 no total
1.000 por diretório
Tamanho máximo de um arquivo de aplicativo 32 megabytes
Tamanho máximo de um arquivo estático 32 megabytes
Tamanho máximo total de todos os arquivos de aplicativo e arquivos estáticos O primeiro 1 gigabyte é gratuito
US$ 0,026 por gigabyte por mês após o primeiro 1 gigabyte
Tempo limite da solicitação pendente 10 segundos
Tamanho máximo de um campo de cabeçalho de solicitação único 8 kilobytes para ambientes de execução de segunda geração no ambiente padrão. As solicitações para esses ambientes de execução com campos de cabeçalho acima de 8 kilobytes retornarão erros HTTP 400.

Limites de solicitações

Todas as solicitações em HTTP/2 são traduzidas para HTTP/1.1 quando encaminhadas para o servidor do aplicativo.

Limites de respostas

  • As respostas dinâmicas são limitadas a 32 MB. Se um gerenciador de script gerar uma resposta maior do que esse limite, o servidor devolverá uma resposta vazia com um código de status 500 Erro interno do servidor. Essa limitação não se aplica a respostas que disponibilizam dados do Blobstore legado ou do Cloud Storage.

  • O limite de cabeçalho de resposta é de 8 KB para ambientes de execução de segunda geração. Os cabeçalhos de resposta que excederem esse limite retornarão erros HTTP 502, com registros que mostram upstream sent too big header while reading response header from upstream.

Cabeçalhos de solicitação

Uma solicitação HTTP recebida inclui os cabeçalhos HTTP enviados pelo cliente. Para fins de segurança, alguns cabeçalhos são limpos ou retificados por proxies intermediários antes de chegarem ao aplicativo.

Para mais informações, consulte a referência sobre cabeçalhos de solicitação.

Como processar os tempos limites das solicitações

O App Engine é otimizado para aplicativos com solicitações de curta duração, normalmente aquelas que levam poucas centenas de milissegundos. Aplicativos eficientes respondem com rapidez à maioria das solicitações. Caso contrário, o aplicativo não escalonará bem com a infraestrutura do App Engine. Para garantir esse nível de desempenho, há um tempo limite máximo de solicitação tributo pelo sistema para cada aplicativo.

Se o aplicativo exceder esse prazo, o App Engine interromperá o gerenciador de solicitações. O Java Runtime Environment interrompe o servlet gerando um com.google.apphosting.api.DeadlineExceededException. Se não houver nenhum gerenciador de solicitações para capturar essa exceção, o ambiente do ambiente de execução retornará um erro de servidor HTTP 500 para o cliente.

Se houver um gerenciador de solicitações e o DeadlineExceededException for capturado, o ambiente de execução concederá tempo ao gerenciador de solicitações (menos de um segundo) para preparar uma resposta personalizada. Se o gerenciador de solicitações levar mais de um segundo depois de gerar a exceção para preparar uma resposta personalizada, um HardDeadlineExceededError será gerado.

DeadlineExceededExceptions e HardDeadlineExceededErrors forçarão o encerramento da solicitação e interromperão a instância.

Para saber quanto tempo resta para o fim do prazo, o aplicativo pode importar com.google.apphosting.api.ApiProxy e chamar ApiProxy.getCurrentEnvironment().getRemainingMillis(). Isso é útil quando o aplicativo está programado para iniciar algum trabalho que possa ser muito demorado. É possível que uma unidade de trabalho leve cinco segundos para ser processada, mas getRemainingMillis() retorna em menos tempo, então não há motivos para iniciar essa unidade de trabalho.

Respostas

O App Engine chama o servlet com um objeto de solicitação e um objeto de resposta, depois aguarda o servlet preencher o objeto de resposta e retornar. Quando o servlet retorna, os dados no objeto de resposta são enviados ao usuário.

Existem limites de tamanho que se aplicam à resposta gerada. Essa resposta pode ser modificada antes de retornar ao cliente.

Para mais informações, consulte a referência sobre respostas a solicitações.

Respostas de streaming

O App Engine não é compatível com respostas de streaming em que os dados são enviados em blocos incrementais para o cliente enquanto uma solicitação está sendo processada. Todos os dados do seu código são coletados conforme descrito acima e enviados como uma única resposta HTTP.

Compactação de resposta

O App Engine faz o possível para exibir conteúdo compactado (gzip) para clientes compatíveis. Para determinar se o conteúdo precisa ser compactado, o App Engine faz o seguinte ao receber uma solicitação:

  1. Confirma se o cliente pode receber respostas compactadas de maneira confiável visualizando os cabeçalhos Accept-Encoding e User-Agent na solicitação. Esse método evita alguns erros conhecidos com conteúdo compactado com gzip nos navegadores mais conhecidos.

  2. Confirma se a compactação do conteúdo é apropriada ao visualizar o cabeçalho Content-Type configurado para o gerenciador de respostas. Em geral, a compactação é apropriada para tipos de conteúdo baseados em texto, e não para tipos de conteúdo binário.

Observe o seguinte:

  • Um cliente pode forçar a compactação de tipos de conteúdo baseados em texto definindo os cabeçalhos da solicitação Accept-Encoding e User-Agent como gzip.

  • Se uma solicitação não especificar gzip no cabeçalho Accept-Encoding, o App Engine não compactará os dados de resposta.

  • O Google Frontend armazena em cache as respostas dos gerenciadores de arquivo e diretório estáticos do App Engine. Dependendo de diversos fatores, como qual tipo de dados de resposta é armazenado em cache primeiro, quais cabeçalhos Vary são especificados na resposta e quais cabeçalhos estão incluídos na solicitação, o cliente pode solicitar dados compactados e recebê-los não compactados e vice-versa. Para mais informações, consulte Armazenamento de respostas em cache.

Armazenamento de respostas em cache

O Google Frontend e, possivelmente, o navegador do usuário e outros servidores proxy de cache intermediários armazenarão as respostas do aplicativo em cache, conforme instruído pelos cabeçalhos de armazenamento em cache padrão especificados na resposta. Especifique esses cabeçalhos de resposta por meio do framework, diretamente no seu código ou por meio de gerenciadores de diretórios e arquivos estáticos do App Engine.

No Google Frontend, a chave de cache é o URL completo da solicitação.

Como armazenar conteúdo estático em cache

Para garantir que os clientes sempre recebam conteúdo estático atualizado assim que ele for publicado, recomendamos exibir conteúdo estático de diretórios com versão, como css/v1/styles.css. O Google Frontend não validará o cache (verifique o conteúdo atualizado) até que ele expire. Mesmo após a expiração, ele não será atualizado até que o conteúdo do URL de solicitação seja alterado.

Os seguintes cabeçalhos de resposta que podem ser definidos em appengine-web.xml influenciam como e quando o Google Frontend armazena conteúdo em cache:

  • Cache-Control precisa ser definido como public para que o Google Frontend armazene conteúdo em cache. Ele também pode ser armazenado em cache pelo Google Frontend, a menos que você especifique uma diretiva Cache-Control private ou no-store. Se você não definir esse cabeçalho em appengine-web.xml, o App Engine irá adicioná-lo automaticamente a todas as respostas processadas por um gerenciador de arquivo ou diretório estático. Para mais informações, consulte Cabeçalhos adicionados ou substituídos.

  • Vary: para permitir que o cache retorne respostas diferentes para um URL com base nos cabeçalhos enviados na solicitação, defina um ou mais dos seguintes valores no cabeçalho da resposta Vary: Accept, Accept-Encoding, Origin ou X-Origin

    Devido ao potencial para alta cardinalidade, os dados não serão armazenados em cache para outros valores de Vary.

    Por exemplo:

    1. Especifique o seguinte cabeçalho de resposta:

      Vary: Accept-Encoding

    2. O aplicativo recebe uma solicitação que contém o cabeçalho Accept-Encoding: gzip. O App Engine retorna uma resposta compactada e o Google Frontend armazena em cache a versão em gzip dos dados da resposta. Todas as solicitações subsequentes deste URL que contenham o cabeçalho Accept-Encoding: gzip receberão os dados compactados em gzip do cache até que ele se torne inválido (devido à alteração do conteúdo após a expiração).

    3. O aplicativo recebe uma solicitação que não contém o cabeçalho Accept-Encoding. O App Engine retorna uma resposta descompactada e o Google Frontend armazena em cache a versão descompactada dos dados de resposta. Todas as solicitações subsequentes deste URL que não contenham o cabeçalho Accept-Encoding receberão os dados compactados do cache até que o ele se torne inválido.

    Se você não especificar um cabeçalho de resposta Vary, o Google Frontend criará uma única entrada de cache para o URL e a usará para todas as solicitações, independentemente dos cabeçalhos na solicitação. Por exemplo:

    1. Você não especifica o cabeçalho de resposta Vary: Accept-Encoding.
    2. Uma solicitação contém o cabeçalho Accept-Encoding: gzip e a versão compactada em gzip dos dados de resposta será armazenada em cache.
    3. Uma segunda solicitação não contém o cabeçalho Accept-Encoding: gzip. No entanto, como o cache contém uma versão compactada em gzip dos dados de resposta, a resposta será compactada, mesmo que o cliente tenha solicitado dados descompactados.

Os cabeçalhos da solicitação também influenciam o armazenamento em cache:

  • Se a solicitação contiver um cabeçalho Authorization, o conteúdo não será armazenado em cache pelo Google Frontend.

Expiração do cache

Por padrão, os cabeçalhos de armazenamento em cache que gerenciadores de arquivo e diretório estáticos do App Engine adicionam às respostas instruem os clientes e os proxies da Web, como o Google Frontend, a expirar o cache após 10 minutos.

Depois que um arquivo é transmitido com um prazo de validade determinado, geralmente, não há como removê-lo de caches de proxy da Web, mesmo que o usuário limpe seu próprio cache do navegador. A reimplantação de uma nova versão do aplicativo não redefinirá nenhum cache. Portanto, se você planeja modificar um arquivo estático, ele deve ter um tempo de expiração curto (menos de uma hora). Na maioria dos casos, o prazo de validade padrão de 10 minutos é suficiente.

É possível alterar a expiração padrão de todos os gerenciadores de arquivos e diretórios estáticos especificando o elemento static-files no arquivo appengine-web.xml.

Geração de registros

O aplicativo grava informações nos próprios registros usando java.util.logging.Logger. Os dados de registro do aplicativo podem ser visualizados no console do Google Cloud usando o Cloud Logging. Cada solicitação registrada recebe um ID de solicitação, um identificador exclusivo global com base no horário de início da solicitação. O console do Google Cloud consegue reconhecer os níveis de registro da classe Logger e exibir mensagens interativamente em níveis diferentes.

Tudo o que o servlet grava no stream de saída padrão (System.out) e no stream de erro padrão (System.err) é capturado pelo App Engine e gravado nos registros do aplicativo. As linhas gravadas no stream de saída padrão são registradas no nível "INFO", e as linhas gravadas no stream de erro padrão são registradas no nível "WARNING". Qualquer biblioteca de registros (como log4j) que grave nos streams de saída ou de erro funcionará. No entanto, para um controle mais minucioso da exibição do nível de registro no console do Google Cloud, a framework de geração de registros precisa usar um adaptador java.util.logging.

// With @WebServlet annotation the webapp/WEB-INF/web.xml is no longer required.
@WebServlet(
    name = "RequestLogging",
    description = "Requests: Logging example",
    urlPatterns = "/requests/log"
)
public class LoggingServlet extends HttpServlet {

  private static final Logger log = Logger.getLogger(LoggingServlet.class.getName());

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    log.info("An informational message.");
    log.warning("A warning message.");
    log.severe("An error message.");
    // ...
  }
}

O Java SDK do App Engine inclui um arquivo logging.properties de modelo no diretório appengine-java-sdk/config/user/. Para usá-lo, copie o arquivo para seu diretório WEB-INF/classes (ou outro lugar no WAR) e a propriedade do sistema java.util.logging.config.file para "WEB-INF/logging.properties" (ou o caminho que você escolher, relativo à raiz do aplicativo). Defina as propriedades do sistema no arquivo appengine-web.xml da seguinte maneira:

<appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> ... <system-properties> <property name="java.util.logging.config.file" value="WEB-INF/logging.properties" /> </system-properties> </appengine-web-app>

O servlet registra mensagens usando o nível de registro INFO (usando log.info()). O nível de registro padrão é WARNING, que suprime mensagens INFO da saída. Para alterar o nível de registro, edite o arquivo logging.properties.

Ambiente

Todas as propriedades do sistema e variáveis de ambiente são particulares ao aplicativo. A configuração de uma propriedade do sistema afeta apenas a visão do seu aplicativo dessa propriedade e não a visão do JVM.

É possível definir propriedades do sistema e variáveis de ambiente para o aplicativo no descritor de implantação.

O App Engine define várias propriedades do sistema que identificam o ambiente de execução:

  • com.google.appengine.runtime.environment é "Production" quando executado no App Engine e "Development" quando executado no servidor de desenvolvimento.

    Além de usar System.getProperty(), é possível acessar as propriedades do sistema usando nossa API de tipo seguro. Por exemplo:

    if (SystemProperty.environment.value() ==
        SystemProperty.Environment.Value.Production) {
        // The app is running on App Engine...
    }
    
  • com.google.appengine.runtime.version é o ID da versão do ambiente de execução, como "1.3.0". Você pode conseguir a versão invocando: String version = SystemProperty.version.get();

  • com.google.appengine.application.id é o ID do aplicativo. Você pode conseguir o ID invocando: String ID = SystemProperty.applicationId.get();

  • com.google.appengine.application.version é a versão principal e secundária do serviço de aplicativo em execução no momento, como "X.Y". O número da versão principal ("X") está especificado no arquivo appengine-web.xml do serviço. O número da versão secundária ("Y") é definido automaticamente quando é feito o upload de cada versão do aplicativo no App Engine. Você pode conseguir o ID invocando: String ID = SystemProperty.applicationVersion.get();

    No servidor da Web de desenvolvimento, a versão principal retornada é sempre a versão do serviço padrão, e a versão secundária é sempre "1".

O App Engine também define as seguintes propriedades do sistema quando inicializa a JVM no servidor de aplicativos:

  • file.separator
  • path.separator
  • line.separator
  • java.version
  • java.vendor
  • java.vendor.url
  • java.class.version
  • java.specification.version
  • java.specification.vendor
  • java.specification.name
  • java.vm.vendor
  • java.vm.name
  • java.vm.specification.version
  • java.vm.specification.vendor
  • java.vm.specification.name
  • user.dir

IDs de instância

Você pode recuperar o ID da instância que processa uma solicitação usando este código:

com.google.apphosting.api.ApiProxy.getCurrentEnvironment().getAttributes().get("com.google.appengine.instance.id")

No ambiente de produção, um administrador conectado pode usar o ID em um URL: https://INSTANCE_ID-dot-VERSION_ID-dot-SERVICE_ID-dot-PROJECT_ID.REGION_ID.r.appspot.com. A solicitação será encaminhada para essa instância específica. Se não conseguir processar a solicitação, a instância retornará um erro 503 imediato.

IDs de solicitação

No momento da solicitação, é possível salvar o código que é exclusivo dela. O código da solicitação poderá ser usado depois para correlacionar uma solicitação aos registros dela.

O seguinte código mostra como conseguir o ID da solicitação no contexto de uma solicitação:

com.google.apphosting.api.ApiProxy.getCurrentEnvironment().getAttributes().get("com.google.appengine.runtime.request_log_id")

Como forçar conexões HTTPS

Por motivos de segurança, todos os aplicativos precisam incentivar os clientes a se conectar por https. Para instruir o navegador a preferir https a http em uma determinada página ou em todo o domínio, defina o cabeçalho Strict-Transport-Security nas respostas. Exemplo:

Strict-Transport-Security: max-age=31536000; includeSubDomains
Para definir esse cabeçalho para qualquer conteúdo estático exibido pelo app, adicione o cabeçalho aos gerenciadores de arquivos estáticos e diretórios do app.

A maioria dos frameworks de aplicativos e servidores da Web é compatível com a definição desse cabeçalho para respostas geradas a partir do seu código. Para informações sobre o cabeçalho Strict-Transport-Security no Spring Boot, consulte o HTTP Strict Transport Security (HSTS).

Como gerenciar o trabalho em segundo plano assíncrono

O trabalho em segundo plano é qualquer trabalho que seu aplicativo execute para uma solicitação depois de ter entregado a resposta HTTP. Evite executar trabalhos em segundo plano no aplicativo e revise seu código para verificar se todas as operações assíncronas foram concluídas antes de enviar a resposta.

Para jobs de longa duração, recomendamos o uso do Cloud Tasks. Com o Cloud Tasks, as solicitações HTTP têm longa duração e retornam uma resposta somente após o término de qualquer trabalho assíncrono.