Esegui la migrazione da Java 8 all'ultimo runtime Java

Questa pagina contiene 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 l'ultima versione supportata di Java, vedi Eseguire l'upgrade di un'applicazione esistente.

Java 8 è deprecato. Non potrai eseguire il deployment delle applicazioni Java 8, anche se la tua organizzazione in precedenza utilizzava una policy dell'organizzazione per riattivare i deployment dei runtime legacy. Le applicazioni Java 8 esistenti continueranno a essere eseguite e a ricevere traffico. Ti consigliamo di eseguire la migrazione all'ultima versione supportata di Java.

La migrazione ai runtime Java di seconda generazione ti consente di utilizzare funzionalità del 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 di App Engine ti consente di accedere a molti servizi e API in bundle legacy, come Memcache, nei runtime Java di seconda generazione. La tua 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 Google Cloud prodotti che offrono funzionalità simili a quelle dei servizi in bundle legacy. Questi Google Cloud prodotti forniscono librerie client di Cloud idiomatiche per Java. Per i servizi in bundle non disponibili come prodotti separati in Google Cloud, come l'elaborazione delle immagini, la ricerca e la messaggistica, puoi utilizzare provider di terze parti o altre soluzioni alternative.

Per saperne di più sulla migrazione ai servizi non in bundle, vedi Eseguire la migrazione dai servizi in bundle.

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

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 in bundle utilizzando il JAR delle API App Engine. Facoltativamente, utilizza prodotti consigliati o servizi di terze parti. Google Cloud

Utilizza appengine-web.xml e web.xml per la configurazione dell'app. Nei file appengine-web.xml e web.xml, configura il campo app-engine-bundled-services per abilitare l'accesso a servizi in bundle specifici.

Potresti anche dover configurare altri file YAML a seconda delle funzionalità utilizzate dall'app.

Utilizza app.yaml per la configurazione dell'app.

Potresti anche dover configurare altri file YAML a seconda delle funzionalità utilizzate dall'app.

Le app vengono eseguite il deployment tramite Jetty. Utilizza il formato WAR per creare il pacchetto dell'app. Le app vengono eseguite il deployment utilizzando il tuo server. Utilizza il formato JAR per creare il pacchetto dell'app. Per saperne di più sulla conversione del file WAR esistente in un JAR eseguibile, vedi Ricreare il pacchetto 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 processo di deployment per utilizzare i runtime Java di seconda generazione:

Differenze principali tra Java 8 e i runtime Java 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 l'app non utilizza i servizi in bundle legacy, devi eseguire 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 per le librerie di sistema e le estensioni del linguaggio
Accesso alla rete esterna
Accesso al file system Accesso in lettura/scrittura a /tmp Accesso in lettura/scrittura a /tmp
Runtime del linguaggio Modificato per App Engine Runtime open source non modificato
Meccanismo di isolamento Sandbox del container basata su gVisor Sandbox del container basata su gVisor
Test con il server di sviluppo locale Supportato Supportato
Configurazione di thread safety Può essere specificata nel file appengine-web.xml. Non può essere specificata 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 scarica il flusso
dopo ogni record.
Cloud Logging standard 2
Supporto per il plug-in DataNucleus 2.x Supportato Non supportato 4

Note:

  1. Se l'app non utilizza i servizi in bundle legacy, i runtime Java di seconda generazione possono eseguire qualsiasi framework Java, a condizione che tu crei il pacchetto di 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 di Spring Boot così com'è. Per altri esempi, consulta la sezione Flessibilità del framework.

    Se l'app utilizza i servizi in bundle legacy, App Engine ne esegue il deployment utilizzando Jetty nello stesso modo del 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ù in bundle con i log delle richieste, ma sono separati in record diversi. Per saperne di più sulla lettura e la scrittura dei log nei runtime Java di seconda generazione, consulta la guida al logging.

  3. Per configurare un'app non thread safe nel runtime Java di seconda generazione, in modo simile all'impostazione di <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 in bundle legacy.

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

Differenze nella memoria utilizzata

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

I runtime di seconda generazione calcolano la memoria utilizzata dall'istanza come la somma di ciò che utilizza un processo dell'applicazione e del numero di file dell'applicazione memorizzati dinamicamente nella cache in memoria. Per evitare che le applicazioni che richiedono molta memoria subiscano arresti anomali dell'istanza a causa del superamento dei limiti di memoria, esegui l'upgrade a una classe di istanza più grande con più memoria.

Differenze nell'utilizzo della CPU

I runtime di seconda generazione possono presentare una baseline di utilizzo della CPU più elevata all'avvio a freddo dell'istanza. A seconda della configurazione di scalabilità di un'applicazione, ciò potrebbe avere effetti collaterali indesiderati, ad esempio un numero di istanze superiore a quello previsto se un'applicazione è configurata per scalare 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 l'inoltro delle intestazioni delle richieste con trattini bassi (ad es. X-Test-Foo_bar) all'applicazione. I runtime di seconda generazione introducono Nginx nell'architettura host. Di conseguenza, i runtime di seconda generazione sono configurati per rimuovere automaticamente le intestazioni con trattini bassi (_). Per evitare problemi con l'applicazione, non utilizzare trattini bassi nelle intestazioni delle richieste dell'applicazione.

Flessibilità del framework

I runtime Java di seconda generazione non includono alcun framework di pubblicazione web, a meno che tu 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 pubblicazione web Jetty.

Nel repository GitHub Google Cloud sono disponibili hello world esempi che utilizzano i framework web Java più diffusi:

Eseguire la migrazione dei formati di file XML a YAML

gcloud CLI non supporta i seguenti formati di file:

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

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

Eseguire la migrazione automatica dei file

Per eseguire la migrazione automatica dei file xml:

  1. Devi avere 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 eseguirne il deployment in produzione.

    Per una conversione riuscita di un file xml di esempio in un file yaml, consulta le schede Eseguire la migrazione manuale dei file.

Eseguire la migrazione manuale dei file

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

cron.yaml

Crea un file cron.yaml con un oggetto cron contenente un elenco di oggetti, ognuno con campi che corrispondono 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 cron.yaml documentazione di riferimento.

dispatch.yaml

Crea un file dispatch.yaml con un oggetto dispatch contenente un elenco di oggetti, ognuno 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 saperne di più, consulta la dispatch.yaml documentazione di riferimento

index.yaml

Crea un file index.yaml con un oggetto indexes contenente un elenco di oggetti, ognuno con campi che corrispondono 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 index.yaml documentazione di riferimento.

queue.yaml

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