Como são processados os pedidos

ID da região

O REGION_ID é um código abreviado que a Google atribui com base na região que seleciona quando cria a sua app. O código não corresponde a um país ou uma província, embora alguns IDs de regiões possam parecer semelhantes aos códigos de países e províncias usados frequentemente. Para apps criadas após fevereiro de 2020, REGION_ID.r está incluído nos URLs do App Engine. Para apps existentes criadas antes desta data, o ID da região é opcional no URL.

Saiba mais acerca dos IDs de regiões.

Este documento descreve como a sua aplicação do App Engine recebe pedidos e envia respostas.

Para mais detalhes, consulte a referência de cabeçalhos e respostas de pedidos.

Se a sua aplicação usar serviços, pode enviar pedidos para um serviço específico ou uma versão específica desse serviço. Para mais informações sobre a capacidade de resposta do serviço, consulte o artigo Como os pedidos são encaminhados.

Processar pedidos

A sua aplicação é responsável por iniciar um servidor Web e processar pedidos. Pode usar qualquer framework Web disponível para a sua linguagem de desenvolvimento.

Quando o App Engine recebe um pedido Web para a sua aplicação, invoca o servlet que corresponde ao URL, conforme descrito no ficheiro web.xml da aplicação no diretório WEB-INF/. Suporta as especificações da API Java Servlet 2.5 ou 3.1 para fornecer os dados do pedido ao servlet e aceitar os dados da resposta.

O App Engine executa várias instâncias da sua aplicação. Cada instância tem o seu próprio servidor Web para processar pedidos. Qualquer pedido pode ser encaminhado para qualquer instância, pelo que os pedidos consecutivos do mesmo utilizador não são necessariamente enviados para a mesma instância. O número de instâncias pode ser ajustado automaticamente à medida que o tráfego muda.

Por predefinição, cada servidor Web processa apenas um pedido de cada vez. Para enviar vários pedidos a cada servidor Web em paralelo, marque a sua aplicação como segura para threads adicionando um elemento <threadsafe>true</threadsafe> ao seu ficheiro appengine-web.xml.

A classe servlet de exemplo seguinte apresenta uma mensagem simples no navegador do utilizador.

// 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");
  }
}

Quotas e limites

O App Engine atribui automaticamente recursos à sua aplicação à medida que o tráfego aumenta. No entanto, isto está sujeito às seguintes restrições:

  • O App Engine reserva capacidade de escalabilidade automática para aplicações com baixa latência, em que a aplicação responde a pedidos em menos de um segundo.

  • As aplicações que estão fortemente limitadas pela CPU também podem incorrer em alguma latência adicional para partilhar recursos de forma eficiente com outras aplicações nos mesmos servidores. Os pedidos de ficheiros estáticos estão isentos destes limites de latência.

Cada pedido recebido para a aplicação conta para o limite de pedidos. Os dados enviados em resposta a um pedido contam para o limite de largura de banda de saída (faturável).

Os pedidos HTTP e HTTPS (seguros) são contabilizados para os limites de pedidos, largura de banda de entrada (faturável) e largura de banda de saída (faturável). A Google Cloud página de detalhes da quota da consola também comunica pedidos seguros, largura de banda de entrada segura e largura de banda de saída segura como valores separados para fins informativos. Apenas os pedidos HTTPS contam para estes valores. Para mais informações, consulte a página Quotas.

Os seguintes limites aplicam-se especificamente à utilização de controladores de pedidos:

Limite Montante
Tamanho do pedido 32 megabytes
Tamanho da resposta 32 megabytes
Tempo limite de pedido Depende do tipo de dimensionamento que a sua app usa
Número total máximo de ficheiros (ficheiros de apps e ficheiros estáticos) 10 000 no total
1000 por diretório
Tamanho máximo de um ficheiro de aplicação 32 megabytes
Tamanho máximo de um ficheiro estático 32 megabytes
Tamanho total máximo de todos os ficheiros estáticos e de aplicações O primeiro gigabyte é gratuito
0,026$ por gigabyte por mês após o primeiro gigabyte
Tempo limite de pedido pendente 10 segundos
Tamanho máximo de um único campo de cabeçalho de pedido 8 kilobytes para tempos de execução de segunda geração no ambiente padrão. Os pedidos a estes tempos de execução com campos de cabeçalho que excedam 8 kilobytes devolvem erros HTTP 400.

Limites aos pedidos

Todos os pedidos HTTP/2 são traduzidos em pedidos HTTP/1.1 quando são encaminhados para o servidor de aplicações.

Limites de respostas

  • As respostas dinâmicas estão limitadas a 32 MB. Se um controlador de scripts gerar uma resposta superior a este limite, o servidor envia uma resposta vazia com um código de estado 500 Internal Server Error. Esta limitação não se aplica a respostas que fornecem dados do Blobstore antigo ou do Cloud Storage.

  • O limite do cabeçalho de resposta é de 8 KB para os tempos de execução de segunda geração. Os cabeçalhos de resposta que excedam este limite devolvem erros HTTP 502, com registos a apresentar upstream sent too big header while reading response header from upstream.

Cabeçalhos do pedido

Um pedido HTTP de entrada inclui os cabeçalhos HTTP enviados pelo cliente. Por motivos de segurança, alguns cabeçalhos são limpos ou alterados por proxies intermédios antes de chegarem à aplicação.

Para mais informações, consulte a referência de cabeçalhos de pedidos.

Processar limites de tempo de pedidos

O App Engine está otimizado para aplicações com pedidos de curta duração, normalmente, aqueles que demoram algumas centenas de milissegundos. Uma app eficiente responde rapidamente à maioria dos pedidos. Uma app que não o faça não é bem dimensionada com a infraestrutura do App Engine. Para garantir este nível de desempenho, existe um limite de tempo de pedido imposto pelo sistema ao qual todas as apps têm de responder.

Se a sua app exceder este prazo, o App Engine interrompe o controlador de pedidos. O ambiente de execução Java interrompe o servlet ao lançar um erro com.google.apphosting.api.DeadlineExceededException. Se não existir um controlador de pedidos para detetar esta exceção, o ambiente de tempo de execução devolve um erro de servidor HTTP 500 ao cliente.

Se existir um controlador de pedidos e o DeadlineExceededException for capturado, o ambiente de tempo de execução dá ao controlador de pedidos tempo (menos de um segundo) para preparar uma resposta personalizada. Se o controlador de pedidos demorar mais de um segundo após gerar a exceção para preparar uma resposta personalizada, é gerado um HardDeadlineExceededError.

Ambos os comandos DeadlineExceededExceptions e HardDeadlineExceededErrors forçam a terminação do pedido e param a instância.

Para saber quanto tempo falta antes do prazo, a aplicação pode importar com.google.apphosting.api.ApiProxy e chamar ApiProxy.getCurrentEnvironment().getRemainingMillis(). Isto é útil se a aplicação planear iniciar algum trabalho que possa demorar demasiado tempo. Se souber que o processamento de uma unidade de trabalho demora cinco segundos, mas getRemainingMillis() devolve menos tempo, não faz sentido iniciar essa unidade de trabalho.

Responses

O App Engine chama o servlet com um objeto de pedido e um objeto de resposta e, em seguida, aguarda que o servlet preencha o objeto de resposta e o devolva. Quando o servlet é devolvido, os dados no objeto de resposta são enviados para o utilizador.

Existem limites de tamanho que se aplicam à resposta que gera, e a resposta pode ser modificada antes de ser devolvida ao cliente.

Para mais informações, consulte a referência de respostas de pedidos.

Respostas graduais

O App Engine não suporta respostas de streaming em que os dados são enviados em blocos incrementais para o cliente enquanto um pedido está a ser processado. Todos os dados do seu código são recolhidos conforme descrito acima e enviados como uma única resposta HTTP.

Compressão de respostas

O App Engine faz o seu melhor para fornecer conteúdo comprimido (com gzip) a clientes que o suportam. Para determinar se o conteúdo deve ser comprimido, o App Engine faz o seguinte quando recebe um pedido:

  1. Confirma se o cliente pode receber respostas comprimidas de forma fiável através da visualização dos cabeçalhos Accept-Encoding e User-Agent no pedido. Esta abordagem evita alguns erros conhecidos com conteúdo comprimido com gzip em navegadores populares.

  2. Confirma se a compressão do conteúdo é adequada através da visualização do cabeçalho Content-Type que configurou para o processador de respostas. Em geral, a compressão é adequada para tipos de conteúdo baseados em texto e não para tipos de conteúdo binários.

Tenha em conta o seguinte:

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

  • Se um pedido não especificar gzip no cabeçalho Accept-Encoding, o App Engine não comprime os dados de resposta.

  • O front-end da Google armazena em cache as respostas dos controladores de ficheiros estáticos e de diretórios do App Engine. Consoante vários fatores, como o tipo de dados de resposta que é colocado em cache primeiro, os cabeçalhos Vary que especificou na resposta e os cabeçalhos incluídos no pedido, um cliente pode pedir dados comprimidos, mas receber dados não comprimidos e vice-versa. Para mais informações, consulte o artigo Colocação em cache de respostas.

Colocação em cache de respostas

O front-end da Google e, potencialmente, o navegador do utilizador e outros servidores proxy de colocação em cache intermédios, vão colocar em cache as respostas da sua app, conforme indicado nos cabeçalhos de colocação em cache padrão que especificar na resposta. Pode especificar estes cabeçalhos de resposta através da sua framework, diretamente no seu código ou através dos processadores de ficheiros estáticos e diretórios do App Engine.

No frontend da Google, a chave da cache é o URL completo do pedido.

Colocar conteúdo estático em cache

Para garantir que os clientes recebem sempre conteúdo estático atualizado assim que é publicado, recomendamos que publique conteúdo estático a partir de diretórios com controlo de versões, como css/v1/styles.css. O front-end da Google não valida a cache (verifica se existe conteúdo atualizado) até a cache expirar. Mesmo após o cache expirar, o cache não é atualizado até que o conteúdo no URL do pedido seja alterado.

Os seguintes cabeçalhos de resposta que pode definir em appengine-web.xml influenciam como e quando o front-end da Google coloca conteúdo em cache:

  • Cache-Control deve ser definido como public para que o front-end da Google coloque em cache o conteúdo. O conteúdo também pode ser colocado em cache pelo front-end da Google, a menos que especifique uma diretiva Cache-Control private ou no-store. Se não definir este cabeçalho em appengine-web.xml, o App Engine adiciona-o automaticamente a todas as respostas processadas por um ficheiro estático ou um controlador de diretório. Para mais informações, consulte o artigo Cabeçalhos adicionados ou substituídos.

  • Vary: Para permitir que a cache devolva respostas diferentes para um URL com base nos cabeçalhos enviados no pedido, defina um ou mais dos seguintes valores no cabeçalho de resposta Vary: Accept, Accept-Encoding, Origin ou X-Origin

    Devido ao potencial de elevada cardinalidade, os dados não são colocados em cache para outros valores de Vary.

    Por exemplo:

    1. Especifique o seguinte cabeçalho da resposta:

      Vary: Accept-Encoding

    2. A sua app recebe um pedido que contém o cabeçalho Accept-Encoding: gzip. O App Engine devolve uma resposta comprimida e o Google Frontend armazena em cache a versão comprimida com gzip dos dados de resposta. Todos os pedidos subsequentes para este URL que contenham o cabeçalho Accept-Encoding: gzip vão receber os dados comprimidos com gzip da cache até que a cache seja invalidada (devido a alterações no conteúdo após a expiração da cache).

    3. A sua app recebe um pedido que não contém o cabeçalho Accept-Encoding. O App Engine devolve uma resposta não comprimida e o Google Frontend armazena em cache a versão não comprimida dos dados de resposta. Todos os pedidos subsequentes para este URL que não contenham o cabeçalho Accept-Encoding vão receber os dados comprimidos da cache até que a cache seja invalidada.

    Se não especificar um cabeçalho de resposta Vary, o front-end da Google cria uma única entrada de cache para o URL e usa-a para todos os pedidos, independentemente dos cabeçalhos no pedido. Por exemplo:

    1. Não especifica o cabeçalho da resposta Vary: Accept-Encoding.
    2. Um pedido contém o cabeçalho Accept-Encoding: gzip e a versão comprimida com gzip dos dados de resposta é colocada em cache.
    3. Um segundo pedido não contém o cabeçalho Accept-Encoding: gzip. No entanto, uma vez que a cache contém uma versão comprimida com gzip dos dados de resposta, a resposta é comprimida com gzip, mesmo que o cliente tenha pedido dados não comprimidos.

Os cabeçalhos no pedido também influenciam o armazenamento em cache:

  • Se o pedido contiver um cabeçalho Authorization, o conteúdo não é colocado em cache pelo front-end da Google.

Expiração da cache

Por predefinição, os cabeçalhos de colocação em cache que os controladores de ficheiros estáticos e de diretórios do App Engine adicionam às respostas indicam aos clientes e aos proxies Web, como o frontend da Google, que a cache expira após 10 minutos.

Depois de um ficheiro ser transmitido com um determinado tempo de expiração, geralmente, não existe forma de o limpar das caches de proxy Web, mesmo que o utilizador limpe a sua própria cache do navegador. A nova implementação de uma nova versão da app não repõe nenhuma cache. Por conseguinte, se alguma vez planear modificar um ficheiro estático, este deve ter um prazo de validade curto (inferior a uma hora). Na maioria dos casos, o tempo de expiração predefinido de 10 minutos é adequado.

Pode alterar a expiração predefinida de todos os gestores de ficheiros e diretórios estáticos especificando o elemento static-files no ficheiro appengine-web.xml.

Registo

A sua aplicação pode escrever informações nos registos da aplicação através de java.util.logging.Logger. Pode ver os dados de registo da sua aplicação na Google Cloud consola através do Cloud Logging. A cada pedido registado é atribuído um ID do pedido, um identificador exclusivo a nível global com base na hora de início do pedido. A consola pode reconhecer os níveis de registo da classe Logger e apresentar interativamente mensagens em diferentes níveis.Google Cloud

Tudo o que o servlet escreve na stream de saída padrão (System.out) e na stream de erro padrão (System.err) é capturado pelo App Engine e registado nos registos da aplicação. As linhas escritas no fluxo de saída padrão são registadas ao nível "INFO" e as linhas escritas no fluxo de erros padrão são registadas ao nível "WARNING". Qualquer framework de registo (como o log4j) que registe nos fluxos de saída ou de erros funciona. No entanto, para um controlo mais detalhado da apresentação do nível de registo na Google Cloud consola, a estrutura de registo tem de 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 SDK Java do App Engine inclui um ficheiro logging.properties de modelo, no diretório appengine-java-sdk/config/user/. Para a usar, copie o ficheiro para o diretório WEB-INF/classes (ou para outro local no WAR) e, em seguida, defina a propriedade do sistema java.util.logging.config.file para "WEB-INF/logging.properties" (ou para o caminho que escolher, relativo à raiz da aplicação). Pode definir propriedades do sistema no ficheiro appengine-web.xml da seguinte forma:

<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 regista mensagens com o nível de registo INFO (usando log.info()). O nível de registo predefinido é WARNING, que suprime as mensagens INFO da saída. Para alterar o nível de registo, edite o ficheiro logging.properties.

O ambiente

Todas as propriedades do sistema e variáveis de ambiente são privadas para a sua aplicação. A definição de uma propriedade do sistema afeta apenas a vista dessa propriedade pela sua aplicação e não a vista da JVM.

Pode definir propriedades do sistema e variáveis de ambiente para a sua app no descritor de implementação.

O App Engine define várias propriedades do sistema que identificam o ambiente de tempo 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(), pode aceder às propriedades do sistema através da nossa API segura em termos de tipo. 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 tempo de execução, como "1.3.0". Pode obter a versão invocando o seguinte: String version = SystemProperty.version.get();

  • com.google.appengine.application.id é o ID da aplicação. Pode obter o ID invocando o seguinte: String ID = SystemProperty.applicationId.get();

  • com.google.appengine.application.version é a versão principal e secundária do serviço de aplicação atualmente em execução, no formato "X.Y". O número da versão principal ("X") é especificado no ficheiro appengine-web.xml do serviço. O número da versão secundária ("Y") é definido automaticamente quando cada versão da app é carregada para o App Engine. Pode obter o ID invocando o seguinte: String ID = SystemProperty.applicationVersion.get();

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

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

  • 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 das instâncias

Pode obter o ID da instância que processa um pedido através deste código:

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

No ambiente de produção, um administrador com sessão iniciada pode usar o ID num URL: https://INSTANCE_ID-dot-VERSION_ID-dot-SERVICE_ID-dot-PROJECT_ID.REGION_ID.r.appspot.com. O pedido é encaminhado para essa instância específica. Se a instância não conseguir processar o pedido, devolve um erro 503 imediato.

IDs de pedidos

No momento do pedido, pode guardar o ID do pedido, que é exclusivo do pedido. O ID do pedido pode ser usado mais tarde para correlacionar um pedido com os registos desse pedido.

O código seguinte mostra como obter o ID do pedido no contexto de um pedido:

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

Forçar ligações HTTPS

Por motivos de segurança, todas as aplicações devem incentivar os clientes a estabelecer ligação através de https. Para instruir o navegador a preferir https em vez de http para uma determinada página ou domínio inteiro, defina o cabeçalho Strict-Transport-Security nas suas respostas. Por exemplo:

Strict-Transport-Security: max-age=31536000; includeSubDomains
Para definir este cabeçalho para qualquer conteúdo estático publicado pela sua app, adicione o cabeçalho aos controladores de ficheiros e diretórios estáticos da app.

A maioria das estruturas de apps e dos servidores Web oferece suporte para definir este cabeçalho para respostas geradas a partir do seu código. Para ver informações sobre o cabeçalho Strict-Transport-Security no Spring Boot, consulte o artigo HTTP Strict Transport Security (HSTS).

Processamento de trabalho assíncrono em segundo plano

O trabalho em segundo plano é qualquer trabalho que a sua app realiza para um pedido depois de ter enviado a resposta HTTP. Evite realizar trabalho em segundo plano na sua app e reveja o código para se certificar de que todas as operações assíncronas terminam antes de enviar a resposta.

Para tarefas de longa duração, recomendamos a utilização do Cloud Tasks. Com o Cloud Tasks, os pedidos HTTP são de longa duração e devolvem uma resposta apenas após a conclusão de qualquer trabalho assíncrono.