Unterschiede zwischen Java 8 und Java 11+

Hinweis: Java 8 hat das Ende des Supports am 31. Januar 2024 erreicht. Ihre vorhandenen Java 8-Anwendungen werden weiterhin ausgeführt und erhalten Traffic. App Engine kann jedoch die erneute Bereitstellung von Anwendungen blockieren, die Laufzeiten nach dem Enddatum des Supports verwenden. Wir empfehlen Ihnen, anhand der Richtlinien auf dieser Seite zur neuesten unterstützten Version von Java zu migrieren.

Durch die Migration zu den Java 11+-Laufzeiten, auch als Java-Laufzeiten der zweiten Generation bezeichnet, können Sie die neuesten Sprachfunktionen verwenden und Anwendungen erstellen, die portabler sind und idiomatischen Code nutzen.

Informationen zu Migrationsoptionen

Zur Reduzierung des Aufwands und der Komplexität der Laufzeitmigration können Sie mit der App Engine-Standardumgebung auf viele gebündelte Legacy-Dienste und -APIs wie Memcache in der Java-Laufzeit der zweiten Generation zugreifen. Ihre Java-Anwendung kann die gebündelten Dienst-APIs über das App Engine API-JAR aufrufen und auf die meisten Funktionen der Java 8-Laufzeitumgebung zugreifen.

Sie können auch Google Cloud-Produkte verwenden, die vergleichbare Funktionen wie die gebündelten Legacy-Dienste bereitstellen. Diese Google Cloud-Produkte bieten idiomatische Cloud-Clientbibliotheken für Java. Für die gebündelten Dienste, die in Google Cloud nicht als separate Produkte verfügbar sind, wie Bildverarbeitung, Suche und Messaging, können Sie Drittanbieter oder andere Problemumgehungen verwenden.

Weitere Informationen zur Migration zu nicht gebündelten Diensten finden Sie unter Von gebündelten Diensten migrieren.

Je nachdem, ob Sie gebündelte Legacy-Dienste verwenden, gibt es einige Unterschiede bei der Ausführung der Laufzeitmigration:

Mit gebündelten Diensten zu Java 11+ migrieren Zu Java 11+ ohne gebündelte Dienste migrieren
Mit der JAR-Datei der App Engine APIs auf gebündelte Dienste zugreifen Optional können Sie empfohlene Google Cloud-Produkte oder Drittanbieterdienste verwenden.

Verwenden Sie appengine-web.xml und web.xml für die Anwendungskonfiguration.

Abhängig von den Features der Anwendung müssen Sie möglicherweise zusätzliche YAML-Dateien konfigurieren.

Verwenden Sie app.yaml für die Anwendungskonfiguration.

Abhängig von den Features der Anwendung müssen Sie möglicherweise zusätzliche YAML-Dateien konfigurieren.

Anwendungen werden über Jetty bereitgestellt. Verwenden Sie das WAR-Format, um Ihre Anwendung zu verpacken. Anwendungen werden mit Ihrem eigenen Server bereitgestellt. Verwenden Sie das JAR-Format, um Ihre Anwendung zu verpacken. Weitere Informationen zum Konvertieren Ihrer vorhandenen WAR-Datei in eine ausführbare JAR-Datei finden Sie unter WAR-Datei verpacken.

Übersicht über den Migrationsprozess

Im Folgenden sind einige Änderungen aufgeführt, die Sie möglicherweise an Ihrer vorhandenen App Engine Java 8-Anwendung und Ihrem Bereitstellungsprozess vornehmen müssen, um die Java-Laufzeit der zweiten Generation zu verwenden:

Hauptunterschiede zwischen den Java 8- und Java 11+-Laufzeiten

Im Folgenden finden Sie eine Zusammenfassung der Unterschiede zwischen den Java 8- und Java 11+-Laufzeiten in der App Engine-Standardumgebung:

Java 8-Laufzeit Java 11+-Laufzeiten
Serverbereitstellung Für Sie über Jetty bereitgestellter Server Wenn Ihre Anwendung die gebündelten Legacy-Dienste nicht verwendet, müssen Sie selbst einen Server bereitstellen.1
Gebündelte App Engine Legacy-Dienste Bereitgestellt Bereitgestellt
Fähigkeit zur Verwendung von Cloud-Clientbibliotheken für Java Ja Ja
Unterstützung von Sprachenerweiterungen und Systembibliotheken Ja Ja
Externer Netzwerkzugriff Ja Ja
Dateisystemzugriff Lese- / Schreibzugriff auf /tmp Lese- / Schreibzugriff auf /tmp
Sprachlaufzeit Für App Engine geändert Unveränderte Open-Source-Laufzeit
Isolationsmechanismus gVisor-basierte Container-Sandbox gVisor-basierte Container-Sandbox
Mit lokalem Entwicklungsserver testen Unterstützt Unterstützt
Thread-Sicherheitskonfiguration Kann in der Datei appengine-web.xml angegeben werden. Kann nicht in den Konfigurationsdateien angegeben werden. Es wird davon ausgegangen, dass alle Apps threadsicher sind.3
Logging Verwendet einen java.util.logging.
ConsoleHandler, der in
stderr schreibt und den Stream
nach jedem Eintrag leert.
Standardmäßiges Cloud Logging2
Unterstützung für DataNucleus-Plug-in 2.x Unterstützt Nicht unterstützt 4

Hinweise:

  1. Wenn Ihre Anwendung keine gebündelten Legacy-Dienste verwendet, können die Java-Laufzeiten der zweiten Generation jedes Java-Framework ausführen, solange Sie einen Webserver verpacken. Dieser ist so konfiguriert, dass er auf HTTP-Anfragen auf dem Port antwortet, der durch die Umgebungsvariable PORT (empfohlen) oder auf Port 8080 angegeben wird. Beispiel: Die Java-Laufzeiten der zweiten Generation können eine Spring Boot Uber-JAR-Datei unverändert ausführen. Weitere Beispiele finden Sie im Abschnitt Framework-Flexibilität.

    Wenn Ihre Anwendung gebündelte Legacy-Dienste verwendet, stellt App Engine sie mithilfe von Jetty auf die gleiche Weise wie in der Java 8-Laufzeit bereit.

  2. Das Logging in Java-Laufzeiten der zweiten Generation folgt dem Logging-Standard in Cloud Logging. In den Java-Laufzeiten der zweiten Generation werden Anwendungslogs nicht mehr mit den Anfragelogs gebündelt, sondern in verschiedenen Einträgen getrennt. Weitere Informationen zum Lesen und Schreiben von Logs in Java-Laufzeiten der zweiten Generation finden Sie in der Logging-Anleitung.

  3. Zum Konfigurieren einer nicht threadsicheren Anwendung in der Java-Laufzeit derzeiten Generation legen Sie ähnlich wie bei der Einstellung von <threadsafe>false</threadsafe> in Java 8 entweder in der app.yaml-Datei oder in der appengine-web.xml-Datei die maximale Gleichzeitigkeit auf 1 fest, wenn die gebündelten Legacy-Dienste verwendet werden.

  4. Google unterstützt die DataNucleus-Bibliothek in Laufzeiten der zweiten Generation nicht. Neuere Versionen von DataNucleus sind mit den in Java 8 verwendeten Versionen abwärtskompatibel. Für den Zugriff auf Datastore empfehlen wir die Clientbibliothek im Datastore-Modus oder die Java-Lösung Objectify (ab Version 6). Objectify ist eine Open-Source-API für Datastore, die eine höhere Abstraktionsebene bietet.

Unterschiede bei der Arbeitsspeichernutzung

Laufzeiten der zweiten Generation haben eine höhere Referenz für die Arbeitsspeichernutzung im Vergleich zu Laufzeiten der ersten Generation. Dies ist auf mehrere Faktoren zurückzuführen, z. B. unterschiedliche Basis-Image-Versionen und Unterschiede bei der Berechnung der Arbeitsspeichernutzung durch die beiden Generationen.

Laufzeiten der zweiten Generation berechnen die Arbeitsspeichernutzung der Instanz als Summe von dem, was von einem Anwendungsprozess verwendet wird, und der Anzahl der Anwendungsdateien, die dynamisch im Arbeitsspeicher zwischengespeichert werden. Führen Sie ein Upgrade auf eine größere Instanzklasse mit mehr Arbeitsspeicher durch, um zu vermeiden, dass es bei arbeitsspeicherintensiven Anwendungen aufgrund des Überschreitens von Arbeitsspeicherlimits zum Herunterfahren von Instanzen kommt.

Unterschiede bei der CPU-Auslastung

Laufzeiten der zweiten Generation können beim Kaltstart von Instanzen eine höhere Referenz der CPU-Auslastung aufweisen. Abhängig von der Skalierungskonfiguration einer Anwendung kann dies unbeabsichtigte Nebenwirkungen haben, z. B. eine höhere Anzahl von Instanzen als erwartet, wenn eine Anwendung für die Skalierung basierend auf der CPU-Auslastung konfiguriert ist. Überprüfen und testen Sie die Konfigurationen der Anwendungsskalierung, damit die Anzahl der Instanzen akzeptabel ist, um dieses Problem zu vermeiden.

Unterschiede beim Anfrageheader

Laufzeiten der ersten Generation ermöglichen, dass Anfrageheader mit Unterstrichen (z.B. X-Test-Foo_bar) an die Anwendung weitergeleitet werden. Laufzeiten der zweiten Generation führen Nginx in die Hostarchitektur ein. Aufgrund dieser Änderung werden Laufzeiten der zweiten Generation so konfiguriert, dass Header mit Unterstrichen (_) automatisch entfernt werden. Vermeiden Sie die Verwendung von Unterstrichen in Anwendungsanfrageheadern, um Anwendungsprobleme zu vermeiden.

Framework-Flexibilität

Die Java-Laufzeiten der zweiten Generation enthalten kein Web-Serving-Framework, es sei denn, Sie verwenden die gebündelten Legacy-Dienste. Dies bedeutet, dass Sie ein anderes Framework als ein Servlet-basiertes Framework verwenden können. Wenn Sie die gebündelten Legacy-Dienste verwenden, stellen die Java-Laufzeiten der zweiten Generation das Jetty-Web-Serving-Framework bereit.

Im Google Cloud GitHub-Repository gibt es hello world-Beispiele, die gängige Java-Web-Frameworks verwenden:

XML-Dateien in YAML-Dateien migrieren

Die gcloud CLI unterstützt folgende Dateiformate nicht:

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

Die folgenden Beispiele zeigen, wie Sie Ihre xml-Dateien in yaml-Dateien migrieren können.

Dateien automatisch migrieren

So migrieren Sie Ihre xml-Dateien automatisch:

  1. Sie benötigen die Version 226.0.0 oder höher der gcloud CLI. So führen Sie ein Update auf die neueste Version aus:

    gcloud components update
    
  2. Geben Sie für jede Datei, die Sie migrieren möchten, einen der folgenden Unterbefehle (cron-xml-to-yaml, datastore-indexes-xml-to-yaml, dispatch-xml-to-yaml, queue-xml-to-yaml ) und den Dateinamen an:

    gcloud beta app migrate-config queue-xml-to-yaml MY-QUEUE-XML-FILE.xml
    
  3. Prüfen Sie manuell die konvertierte Datei, bevor Sie sie in der Produktionsumgebung bereitstellen.

    Eine Beispielkonvertierung von xml zu yaml finden Sie unter den Tabs Dateien manuell migrieren.

Dateien manuell migrieren

So migrieren Sie Ihre xml-Dateien manuell in yaml-Dateien:

cron.yaml

Erstellen Sie eine Datei cron.yaml mit einem cron-Objekt. Dieses muss wiederum Objekte jeweils mit den Feldern enthalten, die den <cron>-Tag-Attributen in Ihrer cron.xml-Datei entsprechen, wie unten gezeigt.

Konvertierte cron.yaml-Datei:

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'

Original cron.xml-Datei:

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

Weitere Informationen finden Sie in der Referenzdokumentation zu cron.yaml.

dispatch.yaml

Erstellen Sie eine dispatch.yaml-Datei mit einem dispatch-Objekt. Dieses muss wiederum Objekte jeweils mit den Feldern enthalten, die den <dispatch>-Tag-Attributen in Ihrer dispatch.xml-Datei entsprechen, wie unten gezeigt.

Konvertierte dispatch.yaml-Datei:

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

Original dispatch.xml-Datei:

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

Weitere Informationen finden Sie in der Referenzdokumentation zu dispatch.yaml.

index.yaml

Erstellen Sie eine index.yaml-Datei mit einem indexes-Objekt. Dieses muss wiederum Objekte jeweils mit den Feldern enthalten, die den <datastore-index>-Tag-Attributen in Ihrer datastore-indexes.xml-Datei entsprechen, wie unten gezeigt.

Konvertierte index.yaml-Datei:

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

Original datastore-index.xml-Datei:

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

Weitere Informationen finden Sie in der Referenzdokumentation zu index.yaml.

queue.yaml

Erstellen Sie eine queue.yaml-Datei mit einem queue-Objekt. Dieses muss wiederum Objekte jeweils mit den Feldern enthalten, die den <queue>-Tag-Attributen in Ihrer queue.xml-Datei entsprechen, wie unten gezeigt.

Konvertierte queue.yaml-Datei:

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

Original queue.xml-Datei:

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