Migre do Java 8 para o runtime Java mais recente

Esta página abrange instruções para a migração dos motores de execução Java de primeira geração para segunda geração. Para atualizar a sua app de segunda geração para usar a versão suportada mais recente do Java, consulte o artigo Atualize uma aplicação existente.

O Java 8 atingiu o fim do suporte a 31 de janeiro de 2024. As suas aplicações Java 8 existentes vão continuar a ser executadas e a receber tráfego. No entanto, o App Engine pode bloquear a nova implementação de aplicações que usam tempos de execução após a data de fim do suporte. Recomendamos que migre para a versão suportada mais recente do Java seguindo as diretrizes nesta página.

A migração para os tempos de execução Java de segunda geração permite-lhe usar funcionalidades de linguagem atualizadas e criar apps mais portáteis com código idiomático.

Compreender as suas opções de migração

Para reduzir o esforço e a complexidade da migração em tempo de execução, o ambiente padrão do App Engine permite-lhe aceder a muitos serviços e APIs agrupados legados, como o Memcache, nos tempos de execução Java de segunda geração. A sua app Java pode chamar as APIs de serviços incluídas através do JAR da API App Engine e aceder à maioria das mesmas capacidades que no tempo de execução do Java 8.

Também tem a opção de usar Google Cloud produtos que oferecem uma funcionalidade semelhante à dos serviços agrupados antigos. Estes Google Cloud produtos oferecem bibliotecas cliente do Google Cloud para Java idiomáticas. Para os serviços incluídos que não estão disponíveis como produtos separados no Google Cloud, como o processamento de imagens, a pesquisa e as mensagens, pode usar fornecedores de terceiros ou outras soluções alternativas.

Para saber mais sobre a migração para serviços desagrupados, consulte o artigo Migrar de serviços agrupados.

Existem algumas diferenças na forma como realiza a migração em tempo de execução, com base no facto de optar por usar ou não os serviços agrupados antigos:

Migrar para os runtimes Java de segunda geração com serviços incluídos Migrar para os runtimes Java de segunda geração sem serviços incluídos
Aceda aos serviços incluídos através do JAR das APIs App Engine. Opcionalmente, use produtos recomendados Google Cloud ou serviços de terceiros.

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

Também pode ter de configurar ficheiros YAML adicionais, consoante as funcionalidades que a sua app usa.

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

Também pode ter de configurar ficheiros YAML adicionais consoante as funcionalidades que a sua app usa.

As apps são implementadas através do Jetty. Use o formato WAR para criar o pacote da sua app. As apps são implementadas através do seu próprio servidor. Use o formato JAR para criar o pacote da sua app. Para saber como converter o seu ficheiro WAR existente num JAR executável, consulte o artigo Voltar a criar o pacote de um ficheiro WAR.

Vista geral do processo de migração

Seguem-se algumas alterações que pode ter de fazer à sua app Java 8 do App Engine existente e ao processo de implementação para usar os runtimes Java de segunda geração:

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

Segue-se um resumo das diferenças entre o Java 8 e os runtimes Java de segunda geração no ambiente padrão do App Engine:

Tempo de execução do Java 8 Tempos de execução Java de segunda geração
Implementação do servidor Servidor implementado para si através do Jetty Se a sua app não usar os serviços incluídos antigos, tem de implementar um servidor por si.1
Serviços agrupados antigos do App Engine Fornecido Fornecido
Capacidade de usar as bibliotecas cliente da Google Cloud para Java Sim Sim
Extensão de idioma e compatibilidade com a biblioteca do sistema Sim Sim
Acesso à rede externa Sim Sim
Acesso ao sistema de ficheiros Acesso de leitura/escrita a /tmp Acesso de leitura/escrita a /tmp
Tempo de execução de idioma Modificado para o App Engine Tempo de execução de código aberto não modificado
Mecanismo de isolamento Sandbox de contentor baseado no gVisor Sandbox de contentor baseado no gVisor
Testes com o servidor de desenvolvimento local Suportado Suportado
Configuração de segurança de threads Pode ser especificado no ficheiro appengine-web.xml. Não pode ser especificado nos ficheiros de configuração. Presume-se que todas as apps são seguras para threads.3
Registo Usa um java.util.logging.
ConsoleHandler, que escreve em
stderr e esvazia a stream
após cada registo.
Cloud Logging padrão 2
Suporte do plug-in DataNucleus 2.x Suportado Não suportado 4

Notas:

  1. Se a sua app não estiver a usar os serviços agrupados antigos, os runtimes Java de segunda geração podem executar qualquer framework Java, desde que agrupe um servidor Web configurado para responder a pedidos HTTP na porta especificada pela variável de ambiente PORT (recomendado) ou na porta 8080. Por exemplo, os runtimes Java de segunda geração podem executar um Spring Boot Uber JAR tal como está. Para ver mais exemplos, consulte a secção Flexibilidade da framework.

    Se a sua app estiver a usar os serviços agrupados antigos, o App Engine implementa-a usando o Jetty da mesma forma que no tempo de execução do Java 8.

  2. O registo dos tempos de execução Java de segunda geração segue a norma de registo no Cloud Logging. Nos runtimes Java de segunda geração, os registos da app já não são agrupados com os registos de pedidos, mas são separados em registos diferentes. Para saber mais sobre a leitura e a escrita de registos nos runtimes Java de segunda geração, consulte o guia de registo.

  3. Para configurar uma app não segura para threads no runtime Java de segunda geração, semelhante à definição de <threadsafe>false</threadsafe> no Java 8, defina a concorrência máxima como 1 no ficheiro app.yaml ou no ficheiro appengine-web.xml, se usar os serviços agrupados antigos.

  4. A Google não suporta a biblioteca DataNucleus nos runtimes de segunda geração. As versões mais recentes do DataNucleus são incompatíveis com as versões usadas no Java 8. Para aceder ao Datastore, recomendamos que use a biblioteca cliente do modo Datastore ou a solução Java Objectify (versão 6 ou posterior). O Objectify é uma API de código aberto para o Datastore que oferece um nível de abstração mais elevado.

Diferenças na utilização de memória

Os tempos de execução de segunda geração têm uma base de utilização de memória mais elevada em comparação com os tempos de execução de primeira geração. Isto deve-se a vários fatores, como diferentes versões de imagens base e diferenças na forma como as duas gerações calculam a utilização de memória.

Os runtimes de segunda geração calculam a utilização de memória da instância como a soma do que um processo de aplicação usa e o número de ficheiros de aplicação armazenados em cache dinamicamente na memória. Para evitar que as aplicações com utilização intensiva de memória sofram encerramentos de instâncias devido à ultrapassagem dos limites de memória, atualize para uma classe de instância maior com mais memória.

Diferenças na utilização da CPU

Os tempos de execução de segunda geração podem observar uma base mais elevada de utilização da CPU no início a frio da instância. Consoante a configuração de escalabilidade de uma aplicação, isto pode ter efeitos secundários não intencionais, como um número de instâncias superior ao previsto, se uma aplicação estiver configurada para ser dimensionada com base na utilização da CPU. Para evitar este problema, reveja e teste as configurações de escalabilidade da aplicação para garantir que o número de instâncias é aceitável.

Diferenças nos cabeçalhos dos pedidos

Os tempos de execução de primeira geração permitem que os cabeçalhos de pedidos com carateres de sublinhado (por exemplo, X-Test-Foo_bar) sejam encaminhados para a aplicação. Os tempos de execução de segunda geração introduzem o Nginx na arquitetura do anfitrião. Como resultado desta alteração, os tempos de execução de segunda geração estão configurados para remover automaticamente os cabeçalhos com carateres de sublinhado (_). Para evitar problemas com a aplicação, evite usar carateres de sublinhado nos cabeçalhos dos pedidos da aplicação.

Flexibilidade da framework

Os runtimes Java de segunda geração não incluem nenhuma framework de publicação na Web, a menos que esteja a usar os serviços incluídos legados. Isto significa que pode usar uma framework diferente de uma framework baseada em servlet. Se estiver a usar os serviços agrupados antigos, os runtimes Java de segunda geração oferecem a framework de serviço Web Jetty.

Existem hello world exemplos que usam frameworks Web Java populares no Google Cloud repositório do GitHub:

Migrar formatos de ficheiros XML para YAML

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

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

Os exemplos seguintes demonstram como migrar os seus ficheiros xml para ficheiros yaml.

Migrar os seus ficheiros automaticamente

Para migrar automaticamente os seus ficheiros xml:

  1. Tem de ter a versão 226.0.0 ou posterior da CLI gcloud. Para atualizar para a versão mais recente:

    gcloud components update
    
  2. Para cada ficheiro que 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 ficheiro:

    gcloud beta app migrate-config queue-xml-to-yaml MY-QUEUE-XML-FILE.xml
    
  3. Verifique manualmente o ficheiro convertido antes de o implementar em produção.

    Para uma conversão bem-sucedida de um ficheiro de exemplo xml para yaml, consulte os separadores Migrar os seus ficheiros manualmente.

Migrar os seus ficheiros manualmente

Para migrar manualmente os seus ficheiros xml para ficheiros yaml:

cron.yaml

Crie um ficheiro cron.yaml com um objeto cron que contenha uma lista de objetos, cada um com campos que correspondam a cada um dos atributos da etiqueta <cron> no seu ficheiro cron.xml, conforme mostrado abaixo.

Ficheiro 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'

Ficheiro 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 cron.yamldocumentação de referência.

dispatch.yaml

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

Ficheiro dispatch.yaml convertido:

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

Ficheiro 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 dispatch.yaml referência

index.yaml

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

Ficheiro 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

Ficheiro 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 index.yamldocumentação de referência.

queue.yaml

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

Ficheiro 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

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