Esegui la migrazione da Java 8 al runtime Java più recente

Questa pagina fornisce istruzioni per eseguire la migrazione dai runtime Java di prima generazione a quelli di seconda generazione. Per eseguire l'upgrade dell'app di seconda generazione in modo che utilizzi l'ultima versione di Java supportata, consulta Eseguire l'upgrade di un'applicazione esistente.

Il 31 gennaio 2024 è stata raggiunta la fine del supporto di Java 8. Le applicazioni Java 8 esistenti continueranno a essere eseguite e a ricevere traffico. Tuttavia, App Engine potrebbe bloccare il rideployment delle applicazioni che utilizzano i runtime dopo la data di fine del supporto. Ti consigliamo di eseguire la migrazione all'ultima versione supportata di Java seguendo le linee guida riportate in questa pagina.

La migrazione ai runtime Java di seconda generazione ti consente di usare funzionalità di linguaggio aggiornate e di creare app più portabili con codice idiomatico.

Informazioni sulle opzioni di migrazione

Per ridurre le attività e la complessità della migrazione del runtime, l'ambiente standard di App Engine consente di accedere a molti dei servizi e delle API in bundle legacy, come Memcache, nei runtime Java di seconda generazione. L'app Java può chiamare le API dei servizi in bundle tramite JAR dell'API App Engine e accedere alla maggior parte delle funzionalità del runtime Java 8.

Hai anche la possibilità di utilizzare i prodotti Google Cloud che offrono funzionalità simili ai servizi in bundle legacy. Questi prodotti Google Cloud forniscono librerie client Cloud per Java idiomatiche. Per i servizi in bundle che non sono disponibili come prodotti separati in Google Cloud, ad esempio elaborazione di immagini, ricerca e messaggistica, puoi utilizzare provider di terze parti o altre soluzioni alternative.

Per scoprire di più sulla migrazione ai servizi non in bundle, consulta Migrazione da servizi in bundle.

Esistono alcune differenze nel modo in cui esegui la migrazione del runtime, a seconda che tu scelga o meno di utilizzare i servizi in bundle legacy:

Migrazione ai runtime Java di seconda generazione con servizi in bundle Migrazione a runtime Java di seconda generazione senza servizi in bundle
Accedi ai servizi in bundle utilizzando il JAR delle API di App Engine. Facoltativamente, utilizza i prodotti Google Cloud consigliati o i servizi di terze parti.

Utilizza appengine-web.xml e web.xml per la configurazione dell'app.

Potresti anche dover configurare file YAML aggiuntivi, a seconda delle funzionalità utilizzate dalla tua app.

Utilizza app.yaml per la configurazione dell'app.

Potresti anche dover configurare file YAML aggiuntivi, a seconda delle funzionalità utilizzate dalla tua app.

Il deployment delle app viene eseguito tramite Jetty. Utilizza il formato WAR per pacchettizzare la tua app. Il deployment delle app viene eseguito usando il tuo server. Utilizza il formato JAR per pacchettizzare la tua app. Per scoprire di più sulla conversione del file WAR esistente in un JAR eseguibile, consulta Ripacchettizzazione di un file WAR.

Panoramica del processo di migrazione

Di seguito sono elencate alcune modifiche che potresti dover apportare all'app Java 8 di App Engine esistente e al tuo processo di deployment per utilizzare i runtime Java di seconda generazione:

Differenze principali tra i runtime Java 8 e di seconda generazione

Di seguito è riportato un riepilogo delle differenze tra i runtime Java 8 e di seconda generazione nell'ambiente standard di App Engine:

Runtime Java 8 Runtime Java di seconda generazione
Deployment del server Server implementato automaticamente tramite Jetty Se la tua app non utilizza i servizi in bundle legacy, devi eseguire personalmente il deployment di un server.1
Servizi in bundle legacy di App Engine Fornito da Google Fornito da Google
Possibilità di utilizzare le librerie client di Cloud per Java
Supporto delle estensioni di lingua e della libreria di sistema
Accesso alla rete esterna
Accesso a Files System Accesso in lettura/scrittura a /tmp Accesso in lettura/scrittura a /tmp
Runtime linguaggio Modificato per App Engine Runtime open source non modificato
Meccanismo di isolamento Sandbox del container basato su gVisor Sandbox del container basato su gVisor
Test con il server di sviluppo locale Supportato Supportato
Configurazione di sicurezza dei thread Può essere specificato nel file appengine-web.xml. Non può essere specificato nei file di configurazione. Si presume che tutte le app siano sicure per i thread.3
Logging Utilizza un'istruzione java.util.logging.
ConsoleHandler, che scrive in
stderr ed esegue il flush del flusso
dopo ogni record.
Cloud Logging standard2
Supporto del plug-in DataNucleus 2.x Supportato Non supportato4

Note

  1. Se la tua app non utilizza i servizi in bundle legacy, i runtime Java di seconda generazione possono eseguire qualsiasi framework Java, purché vengano pacchettizzati un server web configurato per rispondere alle richieste HTTP sulla porta specificata dalla variabile di ambiente PORT (opzione consigliata) o sulla porta 8080. Ad esempio, i runtime Java di seconda generazione possono eseguire un JAR Uber Spring Boot così com'è. Per altri esempi, consulta la sezione Flessibilità del framework.

    Se la tua app usa i servizi in bundle legacy, App Engine ne esegue il deployment tramite Jetty, come nel runtime di Java 8.

  2. Il logging nei runtime Java di seconda generazione segue lo standard di logging in Cloud Logging. Nei runtime Java di seconda generazione, i log delle app non sono più raggruppati con i log delle richieste, ma sono separati in record diversi. Per ulteriori informazioni sulla lettura e sulla scrittura dei log nei runtime Java di seconda generazione, consulta la guida al logging.

  3. Per configurare un'app non sicura per thread nel runtime Java di seconda generazione, in modo simile all'impostazione <threadsafe>false</threadsafe> in Java 8, imposta la contemporaneità massima su 1 nel file app.yaml o nel file appengine-web.xml se utilizzi i servizi in bundle legacy.

  4. Google non supporta la libreria DataNucleus nei runtime di seconda generazione. Le versioni più recenti di DataNucleus sono incompatibili con le versioni precedenti di Java 8. Per accedere a Datastore, ti consigliamo di utilizzare la libreria client in modalità Datastore o la soluzione Java Objectify (versione 6 o successive). Objectify è un'API open source per Datastore che fornisce un livello di astrazione superiore.

Differenze di utilizzo della memoria

I runtime di seconda generazione registrano una base di utilizzo della memoria più elevata rispetto ai runtime di prima generazione. Ciò è dovuto a più fattori, come le diverse versioni delle immagini di base e le differenze nel modo in cui le due generazioni calcolano l'utilizzo della memoria.

I runtime di seconda generazione calcolano l'utilizzo della memoria dell'istanza come somma della quantità utilizzata da un processo dell'applicazione e del numero di file delle applicazioni memorizzati in modo dinamico nella memoria. Per evitare che le applicazioni che usano molta memoria subiscano arresti delle istanze a causa del superamento dei limiti di memoria, esegui l'upgrade a una classe di istanza più grande con più memoria.

Differenze di utilizzo della CPU

I runtime di seconda generazione possono registrare una base di utilizzo più elevata della CPU con l'avvio a freddo dell'istanza. A seconda della configurazione di scalabilità di un'applicazione, ciò potrebbe avere effetti collaterali imprevisti, ad esempio un conteggio delle istanze maggiore del previsto se un'applicazione è configurata per la scalabilità in base all'utilizzo della CPU. Per evitare questo problema, rivedi e testa le configurazioni di scalabilità delle applicazioni per assicurarti che il numero di istanze sia accettabile.

Differenze nell'intestazione della richiesta

I runtime di prima generazione consentono di inoltrare all'applicazione le intestazioni delle richieste con trattini bassi (ad es. X-Test-Foo_bar). I runtime di seconda generazione introducono Nginx nell'architettura host. A seguito di questa modifica, i runtime di seconda generazione sono configurati in modo da rimuovere automaticamente le intestazioni con trattini bassi (_). Per evitare problemi con l'applicazione, evita di utilizzare i trattini bassi nelle intestazioni delle richieste dell'applicazione.

Flessibilità del framework

I runtime Java di seconda generazione non includono alcun framework per la pubblicazione sul web, a meno che tu non utilizzi i servizi in bundle legacy. Ciò significa che puoi utilizzare un framework diverso da uno basato su servlet. Se utilizzi servizi in bundle legacy, i runtime Java di seconda generazione forniscono il framework di servizi web Jetty.

Nel repository GitHub di Google Cloud sono disponibili hello world esempi che utilizzano framework web Java popolari:

Migrazione dei formati di file XML in YAML

gcloud CLI non supporta i seguenti formati file:

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

I seguenti esempi mostrano come eseguire la migrazione dei file xml ai file yaml.

Migrazione automatica dei file

Per eseguire automaticamente la migrazione dei file xml:

  1. Devi avere gcloud CLI versione 226.0.0 o successiva. Per eseguire l'aggiornamento alla versione più recente:

    gcloud components update
    
  2. Per ogni file di cui vuoi eseguire la migrazione, specifica uno dei seguenti sottocomandi (cron-xml-to-yaml, datastore-indexes-xml-to-yaml, dispatch-xml-to-yaml, queue-xml-to-yaml) e il nome del file:

    gcloud beta app migrate-config queue-xml-to-yaml MY-QUEUE-XML-FILE.xml
    
  3. Controlla manualmente il file convertito prima di eseguire il deployment in produzione.

    Per un esempio di conversione di file da xml a yaml, consulta le schede Migrazione manuale dei file.

Migrazione manuale dei file

Per eseguire manualmente la migrazione dei file xml ai file yaml:

cron.yaml

Crea un file cron.yaml con un oggetto cron contenente un elenco di oggetti, ciascuno con campi che corrispondono a ciascuno degli attributi del tag <cron> nel tuo file cron.xml, come mostrato di seguito.

File cron.yaml convertito:

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'

File cron.xml originale:

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

Per ulteriori informazioni, consulta la documentazione di riferimento a cron.yaml.

dispatch.yaml

Crea un file dispatch.yaml con un oggetto dispatch contenente un elenco di oggetti, ciascuno con campi che corrispondono a ciascuno degli attributi del tag <dispatch> nel file dispatch.xml, come mostrato di seguito.

File dispatch.yaml convertito:

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

File dispatch.xml originale

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

Per ulteriori informazioni, consulta la documentazione di riferimento di dispatch.yaml

index.yaml

Crea un file index.yaml con un oggetto indexes contenente un elenco di oggetti, ciascuno con campi che corrispondono a ciascuno degli attributi del tag <datastore-index> nel tuo file datastore-indexes.xml, come mostrato di seguito.

File index.yaml convertito:

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

File datastore-index.xml originale:

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

Per ulteriori informazioni, consulta la documentazione di riferimento a index.yaml.

queue.yaml

Crea un file queue.yaml con un oggetto queue contenente un elenco di oggetti, ciascuno con campi che corrispondono a ciascuno degli attributi del tag <queue> nel file queue.xml, come mostrato di seguito.

File queue.yaml convertito:

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

File queue.xml originale:

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