Migrar do Java 8 para o ambiente de execução do Java mais recente

Nesta página, você verá instruções para migrar ambientes de execução do Java da primeira para a segunda geração. Para fazer upgrade do aplicativo de segunda geração para usar a versão compatível mais recente do Java, consulte Fazer upgrade de um aplicativo existente.

O Java 8 chegou ao fim do suporte em 31 de janeiro de 2024. Os aplicativos Java 8 atuais continuarão a ser executados e recebendo tráfego. No entanto, o App Engine pode bloquear a reimplantação de aplicativos que usam ambientes de execução após o término da data de suporte. Recomendamos que você migre para a versão compatível mais recente do Java seguindo as diretrizes nesta página.

A migração para os ambientes de execução Java de segunda geração permite usar recursos de linguagem atualizados e criar aplicativos mais portáteis, com código idiomático.

Noções básicas sobre opções de migração

Para reduzir o esforço e a complexidade de migração do ambiente de execução, o ambiente padrão do App Engine permite acessar muitos serviços e APIs em pacote legados, como Memcache, nos ambientes de execução Java de segunda geração. O aplicativo Java pode chamar as APIs de serviços em pacote por meio do JAR da API do App Engine e acessar a maioria das mesmas funcionalidades no ambiente de execução do Java 8.

Você também tem a opção de usar produtos do Google Cloud que oferecem funcionalidades semelhantes aos serviços incluídos legados. Esses produtos do Google Cloud oferecem bibliotecas de cliente do Cloud para Java. Para os serviços incluídos que não estão disponíveis como produtos separados no Google Cloud, como processamento de imagens, pesquisa e mensagens, use provedores de terceiros ou outras soluções alternativas.

Para saber mais sobre como migrar para serviços desagrupados, consulte Como migrar de serviços incluídos.

Há algumas diferenças na maneira de executar a migração do ambiente de execução, dependendo de você optar por usar ou não os serviços incluídos legados:

Como migrar para os tempos de execução Java de segunda geração com serviços agrupados Como migrar para os tempos de execução Java de segunda geração sem serviços empacotados
Acesse os serviços incluídos usando o JAR das APIs do App Engine. Opcionalmente, use os produtos do Google Cloud recomendados ou serviços de terceiros.

Use appengine-web.xml e web.xml para a configuração do app.

Também pode ser necessário configurar outros arquivos YAML, dependendo dos recursos usados pelo app.

Use app.yaml para a configuração do app.

Pode ser necessário configurar outros arquivos YAML, dependendo dos recursos usados pelo app.

Os apps são implantados via Jetty. Use o formato WAR para empacotar seu aplicativo. Os aplicativos são implantados usando seu próprio servidor. Use o formato JAR para empacotar o aplicativo. Para saber mais sobre como converter o arquivo WAR existente para um JAR executável, consulte Como reempacotar um arquivo WAR.

Visão geral do processo de migração

Confira a seguir algumas alterações que talvez você precise fazer no aplicativo Java 8 do App Engine existente e no processo de implantação para usar os ambientes de execução do Java de segunda geração:

Principais diferenças entre o Java 8 e os tempos de execução Java de segunda geração

Veja a seguir um resumo das diferenças entre o Java 8 e os tempos de execução do Java de segunda geração no ambiente padrão do App Engine:

Ambiente de execução do Java 8 Tempos de execução Java de segunda geração
Implantação do servidor Servidor implantado para você usando Jetty Se o app não usa os serviços incluídos no pacote, é preciso implantar um servidor por conta própria.1
Serviços agrupados do App Engine legado Fornecido Fornecido
Capacidade de usar bibliotecas de cliente do Cloud para Java Sim Sim
Extensão de linguagem e suporte à biblioteca do sistema Sim Sim
Acesso à rede externa Sim Sim
Acesso ao sistema de arquivos Acesso de leitura/gravação a /tmp Acesso de leitura/gravação a /tmp
Ambiente de execução da linguagem Modificado para o App Engine Ambiente de execução de código aberto não modificado
Mecanismo de isolamento Sandbox de contêiner com base em gVisor Sandbox de contêiner com base em gVisor
Como testar com o servidor de desenvolvimento local Compatível Compatível
Configuração de concorrência segura Pode ser especificado no arquivo appengine-web.xml. Não pode ser especificado nos arquivos de configuração. Consideramos que todos os apps são thread-safe.3
Geração de registros Usa uma java.util.logging.
ConsoleHandler, que grava em
stderr e limpa o stream
após cada registro.
Cloud Logging padrão 2
Suporte ao plug-in DataNucleus 2.x Compatível Não compatível 4

Observações:

  1. Se o aplicativo não estiver usando serviços em pacote legados, os ambientes de execução do Java de segunda geração poderão executar qualquer framework Java, desde que você empacote um servidor da Web configurado para responder a solicitações HTTP na porta especificada pela variável de ambiente PORT (recomendado) ou na porta 8080. Por exemplo, os ambientes de execução do Java de segunda geração podem executar um Uber JAR do Spring Boot como estão. Para mais exemplos, consulte a seção Flexibilidade do framework.

    Se seu aplicativo estiver usando serviços em pacote legados, o App Engine o implantará usando o Jetty da mesma forma que no ambiente de execução do Java 8.

  2. A geração de registros nos ambientes de execução do Java de segunda geração segue o padrão de geração de registros no Cloud Logging. Nos ambientes de execução do Java de segunda geração, os registros de apps não são mais agrupados com os registros de solicitação, mas são separados em registros diferentes. Para saber mais sobre como ler e gravar registros nos ambientes de execução do Java de segunda geração, consulte o guia de geração de registros.

  3. Para configurar um app não thread-safe no ambiente de execução do Java 11 de segunda geração, semelhante à definição de <threadsafe>false</threadsafe> no Java 8, defina a simultaneidade máxima como 1 em qualquer um dos arquivos app.yaml ou appengine-web.xml se estiver usando os serviços agrupados legados.

  4. O Google não oferece suporte à biblioteca do DataNucleus nos ambientes de execução de segunda geração. As versões mais recentes do DataNucleus são incompatíveis com versões anteriores usadas no Java 8. Para acessar o Datastore, recomendamos que você use a biblioteca de cliente do modo Datastore ou a solução Objectify (versão 6 ou mais recente) em Java. A Objectify é uma API de código aberto para o Datastore que oferece um nível maior de abstração.

Diferenças de uso da memória

Os ambientes de execução de segunda geração têm um valor de referência maior do uso de memória em comparação com os da primeira geração. Isso ocorre devido a vários fatores, como versões diferentes da imagem de base e diferenças na forma como as duas gerações calculam o uso de memória.

Os ambientes de execução de segunda geração calculam o uso de memória da instância como a soma do que um processo do aplicativo usa e o número de arquivos de aplicativos armazenados em cache dinamicamente na memória. Para evitar que aplicativos com uso intensivo de memória tenham encerramentos de instâncias por excederem os limites de memória, faça upgrade para uma classe de instância maior com mais memória.

Diferenças de uso da CPU

Os ambientes de execução de segunda geração podem ter um valor de referência mais alto de uso da CPU durante a inicialização a frio da instância. Dependendo da configuração de escalonamento de um aplicativo, isso pode ter efeitos colaterais não intencionais, como uma contagem de instâncias maior do que o previsto se um aplicativo estiver configurado para escalonar com base na utilização da CPU. Para evitar esse problema, revise e teste as configurações de escalonamento do aplicativo para garantir que o número de instâncias seja aceitável.

Diferenças de cabeçalhos de solicitação

Os ambientes de execução de primeira geração permitem que os cabeçalhos das solicitações com sublinhados (por exemplo, X-Test-Foo_bar) sejam encaminhados para o aplicativo. Os ambientes de execução de segunda geração introduzem o Nginx na arquitetura do host. Como resultado dessa mudança, os ambientes de execução de segunda geração são configurados para remover automaticamente cabeçalhos com sublinhados (_). Para evitar problemas no aplicativo, evite usar sublinhados nos cabeçalhos das solicitações.

Flexibilidade do framework

Os ambientes de execução do Java de segunda geração não incluem nenhum framework de veiculação na Web, a menos que você esteja usando os serviços incluídos legados. Isso significa que você pode usar um framework diferente do baseado em servlet. Se você estiver usando os serviços incluídos legados, os ambientes de execução do Java de segunda geração fornecem o framework de veiculação na Web do Jetty.

Há amostras de hello world que usam os conhecidos frameworks da Web em Java no repositório do GitHub do Google Cloud:

Como migrar formatos de arquivo XML para YAML

A CLI gcloud não suporta os seguintes formatos de arquivo:

  • cron.xml
  • datastore-index.xml
  • dispatch.xml
  • queue.xml

Os exemplos a seguir demonstram como migrar seus arquivos xml para yaml.

Como migrar arquivos automaticamente

Para migrar seus arquivos xml automaticamente:

  1. É preciso ter a CLI gcloud versão 226.0.0 ou mais recente. Se quiser atualizar para a versão mais recente:

    gcloud components update
    
  2. Para cada arquivo que você quer migrar, especifique um dos seguintes subcomandos (cron-xml-to-yaml, datastore-indexes-xml-to-yaml, dispatch-xml-to-yaml, queue-xml-to-yaml) e o nome do arquivo:

    gcloud beta app migrate-config queue-xml-to-yaml MY-QUEUE-XML-FILE.xml
    
  3. Verifique mais uma vez manualmente o arquivo convertido antes de implantar no ambiente de produção.

    Para uma amostra de conversão de arquivo xml em yaml, consulte as guias em Como migrar seus arquivos manualmente.

Como migrar arquivos manualmente

Para migrar manualmente seus arquivos xml para yaml:

cron.yaml

Crie um arquivo cron.yaml com um objeto cron que contenha uma lista de objetos, cada um com campos que correspondem a cada um dos atributos de tag <cron> no seu arquivo cron.xml, conforme mostrado abaixo.

Arquivo cron.yaml convertido:

cron:
- url: '/recache'
  schedule: 'every 2 minutes'
  description: 'Repopulate the cache every 2 minutes'
- url: '/weeklyreport'
  schedule: 'every monday 08:30'
  target: 'version-2'
  timezone: 'America/New_York'
  description: 'Mail out a weekly report'

Arquivo cron.xml original

<?xml version="1.0" encoding="UTF-8"?>
<cronentries>
  <cron>
    <url>/recache</url>
    <description>Repopulate the cache every 2 minutes</description>
    <schedule>every 2 minutes</schedule>
  </cron>
  <cron>
    <url>/weeklyreport</url>
    <description>Mail out a weekly report</description>
    <schedule>every monday 08:30</schedule>
    <timezone>America/New_York</timezone>
    <target>version-2</target>
  </cron>
</cronentries>

Para mais informações, consulte a documentação de referência do cron.yaml.

dispatch.yaml

Crie um arquivo dispatch.yaml com um objeto dispatch que contenha uma lista de objetos, cada um com campos que correspondem a cada um dos atributos de tag <dispatch> no seu arquivo dispatch.xml, conforme mostrado abaixo.

Arquivo dispatch.yaml convertido:

dispatch:
- url: '*/favicon.ico'
  module: default
- url: 'simple-sample.uc.r.appspot.com/'
  module: default
- url: '*/mobile/*'
  module: mobile-frontend

Arquivo dispatch.xml original

<?xml version="1.0" encoding="UTF-8"?>
<dispatch-entries>
  <dispatch>
      <url>*/favicon.ico</url>
      <module>default</module>
  </dispatch>
  <dispatch>
      <url>simple-sample.uc.r.appspot.com/</url>
      <module>default</module>
  </dispatch>
  <dispatch>
      <url>*/mobile/*</url>
      <module>mobile-frontend</module>
  </dispatch>
</dispatch-entries>

Para mais informações, consulte a documentação de referência do dispatch.yaml.

index.yaml

Crie um arquivo index.yaml com um objeto indexes contendo uma lista de objetos, cada um com campos que correspondem a cada um dos atributos de tag <datastore-index> no seu arquivo datastore-indexes.xml, conforme mostrado abaixo.

Arquivo index.yaml convertido:

indexes:
- ancestor: false
  kind: Employee
  properties:
  - direction: asc
    name: lastName
  - direction: desc
    name: hireDate
- ancestor: false
  kind: Project
  properties:
  - direction: asc
    name: dueDate
  - direction: desc
    name: cost

Arquivo datastore-index.xml original

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes
 autoGenerate="true">
   <datastore-index kind="Employee" ancestor="false">
       <property name="lastName" direction="asc" />
       <property name="hireDate" direction="desc" />
   </datastore-index>
   <datastore-index kind="Project" ancestor="false">
       <property name="dueDate" direction="asc" />
       <property name="cost" direction="desc" />
   </datastore-index>
</datastore-indexes>

Para mais informações, consulte a documentação de referência do index.yaml.

queue.yaml

Crie um arquivo queue.yaml com um objeto queue que contenha uma lista de objetos, cada um com campos que correspondem a cada um dos atributos de tag <queue> no seu arquivo queue.xml, conforme mostrado abaixo.

Arquivo queue.yaml convertido:

queue:
- name: fooqueue
  mode: push
  rate: 1/s
  retry_parameters:
    task_retry_limit: 7
    task_age_limit: 2d
- name: barqueue
  mode: push
  rate: 1/s
  retry_parameters:
    min_backoff_seconds: 10
    max_backoff_seconds: 200
    max_doublings: 0

Arquivo queue.xml original

<queue-entries>
  <queue>
    <name>fooqueue</name>
    <rate>1/s</rate>
    <retry-parameters>
      <task-retry-limit>7</task-retry-limit>
      <task-age-limit>2d</task-age-limit>
    </retry-parameters>
  </queue>
  <queue>
    <name>barqueue</name>
    <rate>1/s</rate>
    <retry-parameters>
      <min-backoff-seconds>10</min-backoff-seconds>
      <max-backoff-seconds>200</max-backoff-seconds>
      <max-doublings>0</max-doublings>
    </retry-parameters>
  </queue>
<queue-entries>