Esegui la migrazione da Java 8 all'ultimo runtime Java

Questa pagina illustra le istruzioni per 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 la versione più recente di Java supportata, vedi Eseguire l'upgrade di un'applicazione esistente.

Java 8 ha raggiunto la fine del supporto il 31 gennaio 2024. Le tue applicazioni Java 8 esistenti continueranno a essere eseguite e a ricevere traffico. Tuttavia, App Engine potrebbe bloccare il nuovo deployment delle applicazioni che utilizzano i runtime dopo la data di fine del supporto. Ti consigliamo di eseguire la migrazione alla versione più recente di Java supportata utilizzando le linee guida riportate in questa pagina.

La migrazione agli ambienti di runtime Java di seconda generazione ti consente di utilizzare funzionalità di linguaggio aggiornate e di creare app più portatili, con codice idiomatico.

Informazioni sulle opzioni di migrazione

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

Hai anche la possibilità di utilizzare i prodotti Google Cloud che offrono funzionalità simili a quelle dei servizi in bundle precedenti. Questi prodotti Google Cloud forniscono librerie client di Cloud per Java idiomatiche. Per i servizi in bundle che non sono disponibili come prodotti separati in Google Cloud, come l'elaborazione di immagini, la ricerca e la messaggistica, puoi utilizzare fornitori di terze parti o altre soluzioni alternative.

Per scoprire di più sulla migrazione ai servizi slegati, consulta Eseguire la migrazione dai servizi in bundle.

Esistono alcune differenze nella modalità di esecuzione della migrazione del runtime, a seconda che tu scelga di utilizzare i servizi legacy in bundle:

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

Utilizza appengine-web.xml e web.xml per la configurazione delle app.

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

Usa app.yaml per la configurazione delle app.

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

Le app vengono implementate tramite Jetty. Utilizza il formato WAR per pacchettizzare l'app. Le app vengono implementate utilizzando il tuo server. Utilizza il formato JAR per pacchettizzare la tua app. Per scoprire di più sulla conversione del file WAR esistente in un file 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 App Engine Java 8 esistente e alla procedura di implementazione per utilizzare i runtime Java di seconda generazione:

Differenze principali tra il runtime Java 8 e quello di seconda generazione

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

Runtime Java 8 Runtime Java di seconda generazione
Deployment del server Server di cui è stato eseguito il deployment utilizzando Jetty Se la tua app non utilizza i servizi pacchettizzati precedenti, devi eseguire il deployment di un server autonomamente.1
Servizi integrati 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 delle librerie di sistema
Accesso alla rete esterna
Accesso al file system Accesso in lettura/scrittura a /tmp Accesso in lettura/scrittura a /tmp
Runtime della lingua Modificato per App Engine Ambiente di runtime open source non modificato
Meccanismo di isolamento Sandbox dei container basata su gVisor Sandbox dei container basata su gVisor
Test con il server di sviluppo locale Supportato Supportato
Configurazione della sicurezza a thread Può essere specificato nel file appengine-web.xml. Non può essere specificato nei file di configurazione. Si presume che tutte le app siano thread-safe.3
Logging Utilizza un java.util.logging.
ConsoleHandler, che scrive in
stderr e svuota lo stream
dopo ogni record.
Cloud Logging standard 2
Supporto del plug-in DataNucleus 2.x Supportato Non supportato 4

Note:

  1. Se la tua app non utilizza i servizi legacy in bundle, i runtime Java di seconda generazione possono eseguire qualsiasi framework Java, a condizione che tu pacchetti un server web configurato per rispondere alle richieste HTTP sulla porta specificata dalla variabile di ambiente PORT (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 utilizza i servizi integrati precedenti, App Engine la esegue in modo simile al runtime 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 scoprire di più sulla lettura e sulla scrittura dei log nei runtime Java di seconda generazione, consulta la guida alla registrazione.

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

  4. Google non supporta la libreria DataNucleus nei runtime di seconda generazione. Le versioni più recenti di DataNucleus non sono compatibili con le versioni precedenti utilizzate in 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 nell'utilizzo della memoria

I runtime di seconda generazione hanno una base di riferimento dell'utilizzo della memoria più elevata rispetto ai runtime di prima generazione. Ciò è dovuto a diversi fattori, come versioni diverse delle immagini di base e 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 la somma di ciò che viene utilizzato da un processo dell'applicazione e del numero di file dell'applicazione memorizzati nella cache dinamicamente in memoria. Per evitare che le applicazioni che utilizzano molta memoria vengano chiuse a causa del superamento dei limiti di memoria, esegui l'upgrade a una classe di istanze più grande con più memoria.

Differenze nell'utilizzo della CPU

I runtime di seconda generazione possono mostrare una linea di base più elevata dell'utilizzo della CPU al primo avvio dell'istanza. A seconda della configurazione della scalabilità di un'applicazione, questo potrebbe avere effetti collaterali indesiderati, ad esempio un numero di istanze superiore a quello previsto se un'applicazione è configurata per la scalabilità in base all'utilizzo della CPU. Per evitare questo problema, esamina e testa le configurazioni di scalabilità dell'applicazione per assicurarti che il numero di istanze sia accettabile.

Differenze nelle intestazioni delle richieste

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

Flessibilità del framework

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

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

Migrazione dei formati file XML a YAML

Gcloud CLI non supporta i seguenti formati file:

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

Gli esempi riportati di seguito mostrano come eseguire la migrazione dei file xml ai file yaml.

Eseguire la migrazione dei file automaticamente

Per eseguire la migrazione automatica dei file xml:

  1. Devi disporre di gcloud CLI versione 226.0.0 o successive. Per eseguire l'aggiornamento all'ultima versione:

    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 una conversione di file di esempio xml in yaml riuscita, consulta le schede Eseguire la migrazione dei file manualmente.

Eseguire la migrazione dei file manualmente

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

cron.yaml

Crea un file cron.yaml con un oggetto cron contenente un elenco di oggetti, ciascuno con campi corrispondenti a ciascuno degli attributi del tag <cron> nel 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 saperne di più, consulta la documentazione di riferimento di cron.yaml.

dispatch.yaml

Crea un file dispatch.yaml con un oggetto dispatch contenente un elenco di oggetti, ciascuno con campi corrispondenti 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 saperne di più, 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 corrispondenti a ciascuno degli attributi del tag <datastore-index> nel 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 saperne di più, consulta la documentazione di riferimento di index.yaml.

queue.yaml

Crea un file queue.yaml con un oggetto queue contenente un elenco di oggetti, ciascuno con campi corrispondenti 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>