Migrer votre application App Engine de Java 8 vers Java 11

En plus de l'environnement d'exécution Java 8, App Engine permet désormais d'héberger vos applications à l'aide de l'environnement d'exécution Java 11. Découvrez comment migrer votre application App Engine existante de Java 8 vers Java 11 dans l'environnement standard.

Principales différences entre les environnements d'exécution Java 8 et Java 11

  • À partir de l'environnement d'exécution Java 11, l'environnement standard App Engine n'inclut plus les services App Engine groupés tels que Memcache et les files d'attente de tâches. À la place, Google Cloud fournit des produits autonomes équivalents à la plupart des services fournis dans l'environnement d'exécution Java 11. Chacun de ces produits Google Cloud fournit des bibliothèques clientes Cloud Java idiomatiques. Pour les services groupés qui ne sont pas disponibles en tant que produits distincts dans Google Cloud, tels que le traitement des images, la recherche et la messagerie, vous pouvez faire appel à des fournisseurs tiers ou à d'autres solutions, comme indiqué dans ce guide de migration.

    La suppression des services App Engine groupés permet à l'environnement d'exécution Java 11 d'offrir une expérience de développement Java entièrement idiomatique. Dans l'environnement d'exécution Java 11, vous écrivez une application Java standard entièrement portable et pouvant s'exécuter dans n'importe quel environnement Java standard, y compris App Engine.

  • Dans l'environnement d'exécution Java 11, les fichiers de configuration sont au format YAML au lieu du format XML. Par exemple, vous spécifiez les paramètres de votre application dans le fichier app.yaml au lieu du fichier appengine-web.xml. Vous devez convertir manuellement le fichier appengine-web.xml en fichier app.yaml. Pour en savoir plus, consultez la page Créer un fichier app.yaml.

    Pour en savoir plus sur la conversion d'autres types de fichiers de configuration App Engine au format YAML, consultez la page Migrer des fichiers XML au format YAML.

  • L'environnement Java 11 peut exécuter n'importe quel framework Java tant que vous empaquetez un serveur Web configuré pour répondre aux requêtes HTTP sur le port spécifié par la variable d'environnement PORT (recommandée) ou sur le port 8080. Par exemple, l'environnement Java 11 peut exécuter un Spring Boot UberJAR tel quel.

Créer un fichier app.yaml

Dans l'environnement d'exécution Java 11, les paramètres de l'application sont stockés dans le fichier app.yaml. Vous devez supprimer le fichier appengine-web.xml de votre application existante et le remplacer par un fichier app.yaml.

Le seul élément obligatoire dans un fichier app.yaml est l'élément runtime :

runtime: java11
# No need for an entrypoint with single fatjar with correct manifest class-path entry.

App Engine fournit des valeurs par défaut pour tous les autres paramètres, y compris la classe d'instance F1 qui détermine les ressources de mémoire et de processeur disponibles pour votre application, et le scaling automatique qui contrôle comment et quand les instances de votre application sont créées.

La commande gcloud app deploy peut créer un fichier app.yaml lorsque vous déployez votre application. Le fichier app.yaml créé par App Engine ne contient que l'entrée runtime. Votre application utilisera des valeurs par défaut pour tous les paramètres.

Si vous devez remplacer les paramètres par défaut, créez un fichier app.yaml et spécifiez les paramètres nécessaires. Pour en savoir plus, consultez la documentation de référence du fichier app.yaml.

Pour un projet Maven, l'emplacement standard du fichier app.yaml se trouve dans le répertoire src/main/appengine. Le plug-in App Engine Maven crée un répertoire target/appengine-staging approprié contenant vos artefacts JAR et ce fichier app.yaml, prêt à être déployé.

Voici un exemple de structure de projet Maven :

MyDir/
  pom.xml
  [index.yaml]
  [cron.yaml]
  [dispatch.yaml]
  src/main/
    appengine/
      app.yaml
    java/com.example.mycode/
      MyCode.java

Si votre répertoire de projet comporte plusieurs fichiers JAR ou si vous souhaitez spécifier un point d'entrée personnalisé, vous devez le spécifier dans l'élément entrypoint de votre fichier app.yaml.

Réempaqueter un fichier WAR dans un fichier JAR

Pour effectuer la migration vers l'environnement d'exécution Java 11, vous devez réempaqueter votre application Web App Engine Java 8 dans un fichier JAR exécutable.

Votre application doit comporter une classe Main qui démarre un serveur Web répondant aux requêtes HTTP sur le port spécifié par la variable d'environnement PORT, généralement 8081.

Exemple :

import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;

public class Main {

  public static void main(String[] args) throws IOException {
    // Create an instance of HttpServer bound to port defined by the
    // PORT environment variable when present, otherwise on 8080.
    int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "8080"));
    HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);

    // Set root URI path.
    server.createContext("/", (var t) -> {
      byte[] response = "Hello World from Google App Engine Java 11.".getBytes();
      t.sendResponseHeaders(200, response.length);
      try (OutputStream os = t.getResponseBody()) {
        os.write(response);
      }
    });

    // Start the server.
    server.start();
  }
}

Exemple de migration WAR

Les instructions suivantes montrent comment réempaqueter une application Java 8 hello-world App Engine en tant que fichier JAR à exécuter sur l'environnement d'exécution Java 11.

La migration utilise l'artefact appengine-simple-jetty-main. Cela fournit une classe Main avec un serveur Web Jetty simple qui charge un fichier WAR et empaquette votre application dans un fichier JAR exécutable :

  1. Clonez l'artefact Embedded Jetty Server sur votre ordinateur local :

    git clone https://github.com/GoogleCloudPlatform/java-docs-samples
    

    Vous pouvez également télécharger l'exemple en tant que fichier zip et l'extraire.

  2. Accédez au répertoire qui contient l'exemple de code :

    cd java-docs-samples/appengine-java11/appengine-simple-jetty-main/
    
  3. Installez la dépendance en local :

    mvn install
    
  4. Ajoutez le code suivant dans le fichier pom.xml de votre projet :

    • Dépendance appengine-simple-jetty-main :
      <dependency>
        <groupId>com.example.appengine.demo</groupId>
        <artifactId>simple-jetty-main</artifactId>
        <version>1</version>
        <scope>provided</scope>
      </dependency>
    • Plug-in maven-dependency :
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>3.1.2</version>
        <executions>
          <execution>
            <id>copy</id>
            <phase>prepare-package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>
                ${project.build.directory}/appengine-staging
              </outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>
      App Engine déploie les fichiers situés dans le répertoire ${build.directory}/appengine-staging. En ajoutant le plug-in maven-dependency à votre compilation, App Engine installe les dépendances spécifiées dans le dossier approprié.
  5. Créez un élément entrypoint dans votre fichier app.yaml pour appeler l'objet appengine-simple-jetty-main et transmettre votre fichier WAR en tant qu'argument. Par exemple, consultez l'exemple helloworld-servlet du fichier app.yaml :

    runtime: java11
    entrypoint: 'java -cp "*" com.example.appengine.demo.jettymain.Main helloworld.war'
  6. Pour exécuter votre application localement, procédez comme suit :

    1. Empaqueter votre application :

      mvn clean package
      
    2. Démarrez le serveur avec votre fichier WAR en tant qu'argument.

      Par exemple, vous pouvez démarrer le serveur dans l'exemple helloworld-servlet en exécutant la commande suivante à partir du dossier java-docs-samples/appengine-java11/appengine-simple-jetty-main/ :

      mvn exec:java -Dexec.args="../helloworld-java8/target/helloworld.war"
      
    3. Dans votre navigateur Web, saisissez l'adresse suivante :

      http://localhost:8080

  7. Pour déployer votre application, procédez comme suit :

    Outils gcloud

    gcloud app deploy

    Plug-in Maven

    mvn package appengine:deploy -Dapp.deploy.projectId=PROJECT_ID

    Remplacez PROJECT_ID par l'ID de votre projet Cloud. Si votre ID de projet est déjà inclus dans le fichier pom.xml, vous n'avez pas besoin d'inclure la propriété -Dapp.deploy.projectId dans la commande que vous exécutez.

Souplesse du framework

L'environnement d'exécution Java 11 n'inclut aucun framework de diffusion Web, ce qui signifie que vous n'êtes plus limité aux frameworks basés sur des servlets.

Le dépôt Google Cloud GitHub contient des exemples hello world utilisant des frameworks Web Java populaires :

Effectuer une migration à partir du SDK Java App Engine

L'environnement d'exécution Java 11 n'est pas compatible avec le SDK Java App Engine. Voici quelques modifications que vous devrez apporter à votre application App Engine Java 8 et votre processus de déploiement pour utiliser l'environnement d'exécution App Engine Java 11 :

Migrer à partir des API spécifiques à App Engine

La plupart des fonctionnalités fournies par les API spécifiques à App Engine sont désormais fournies par la bibliothèque cliente Google Cloud pour Java. Pour plus d'informations, consultez les alternatives recommandées ci-dessous.

Blobstore

Pour stocker et récupérer des données, utilisez Cloud Storage via les bibliothèques clientes Cloud. Pour commencer, consultez la page Utiliser Cloud Storage.

Datastore

Pour remplacer une base de données valeur/clé NoSQL telle que l'API Datastore, utilisez Cloud Firestore en mode Datastore. Firestore est la dernière version de Datastore et le mode Datastore est recommandé pour les bases de données utilisées principalement par les applications App Engine.

Images

Vous pouvez diffuser des images à partir de Cloud Storage, les diffuser directement ou utiliser un réseau de diffusion de contenu tiers (CDN).

Pour redimensionner, convertir et manipuler des images, utilisez une bibliothèque de traitement d'image telle que ImageJ2, imgscalr ou thumbnailator. Pour utiliser l'une de ces bibliothèques tierces, ajoutez-la en tant que dépendance et mettez à jour votre code pour appeler les API de la bibliothèque.

Le service Images App Engine fournit également une fonctionnalité permettant d'éviter les requêtes dynamiques adressées à votre application en gérant le redimensionnement des images à l'aide d'une URL de diffusion. Si vous souhaitez disposer de fonctionnalités similaires, vous pouvez générer les images redimensionnées à l'avance et les importer dans Cloud Storage pour les diffuser. Vous pouvez également utiliser un service de réseau de diffusion de contenu tiers (CDN, Content Delivery Network) permettant le redimensionnement des images.

Logging

Nous vous recommandons de mettre à jour votre application de sorte qu'elle utilise Cloud Logging, qui est compatible avec des fonctionnalités, telles que l'affichage des journaux dans la visionneuse de journaux, le téléchargement des journaux, le filtrage des messages par niveau de gravité et la mise en corrélation des messages de l'application avec des requêtes spécifiques. Vous pouvez également activer ces fonctionnalités en écrivant des messages de journal contenant des données spécifiques structurées dans un objet JSON.

Pour en savoir plus, consultez la section Écrire et afficher des journaux.

Mail

Pour envoyer un e-mail, utilisez un fournisseur de messagerie tiers tel que SendGrid, Mailgun ou Mailjet. Tous ces services proposent des API permettant d'envoyer des e-mails à partir d'applications.

Memcache

Pour mettre en cache des données d'application, utilisez Memorystore pour Redis.

Modules

Pour obtenir des informations et modifier les services en cours d'exécution de votre application, utilisez une combinaison de variables d'environnement et de l'API Admin App Engine :

Informations sur les services Moyen d'accès
ID d'application actuel Variable d'environnement GAE_APPLICATION
ID de projet actuel Variable d'environnement GOOGLE_CLOUD_PROJECT
Nom actuel du service Variable d'environnement GAE_SERVICE
Version actuelle du service Variable d'environnement GAE_VERSION
ID actuel de l'instance Variable d'environnement GAE_INSTANCE
Nom d'hôte par défaut Méthode apps.get de l'API Admin
Liste des services Méthode apps.services.list de l'API Admin
Liste des versions d'un service Méthode apps.services.versions.list de l'API Admin
Version par défaut d'un service, y compris les répartitions du trafic Méthode apps.services.get de l'API Admin
Liste des instances en cours d'exécution d'une version Méthode apps.services.versions.instances.list de l'API Admin
Pour plus d'informations sur les données disponibles sur les services en cours d'exécution de votre application, consultez la page Environnement d'exécution Java 11.

Espaces de noms

L'API Namespaces permettait aux applications mutualisées de partitionner les données entre locataires en spécifiant simplement une chaîne d'espace de noms unique pour chaque locataire.

Tandis que Datastore prend directement en charge la mutualisation, ce n'est pas le cas des autres services Google Cloud. Si votre application mutualisée utilise d'autres services Google Cloud, vous devez gérer la mutualisation manuellement. Pour disposer d'instances de services complètement isolées, vous pouvez créer des projets de manière automatisée à l'aide de l'API Cloud Resource Manager et accéder aux ressources de tous les projets.

OAuth

Au lieu d'utiliser le service OAuth App Engine pour vérifier les jetons OAuth 2.0, utilisez la méthode oauth2.tokeninfo de l'API OAuth 2.0.

Vous pouvez héberger les bases de données de recherche en texte intégral du type ElasticSearch dans Compute Engine et y accéder à partir de votre service.

File d'attente de tâches

Mettez en file d'attente les tâches d'exécution de code asynchrone à l'aide de l'API REST Cloud Tasks, de l'API RPC ou de la bibliothèque cliente Google Cloud, et utilisez un service standard Java 11 App Engine en tant que cible Push. Pour plus d'informations, consultez la page Migrer des files d'attente de tâches vers Cloud Tasks.

Dans la plupart des cas où vous pouvez utiliser des files d'attente de retrait, telles que la mise en file d'attente des tâches ou des messages retirés et traités par des nœuds de calcul distincts, Pub/Sub peut être une bonne alternative, offrant des fonctionnalités et des garanties de livraison semblables. Consultez l'exemple de programme suivant qui interagit avec l'API Cloud Tasks.

Authentification des utilisateurs

Comme alternative à l'API Users, utilisez n'importe quel mécanisme d'authentification basé sur HTTP, par exemple :

Migrer des fichiers XML vers le format YAML

Le SDK Cloud n'accepte pas les formats de fichier suivants :

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

Les exemples suivants montrent comment migrer vos fichiers xml vers des fichiers yaml.

Migrer automatiquement des fichiers

Pour migrer vos fichiers xml automatiquement, procédez comme suit :

  1. Vous devez disposer du SDK Cloud version 226.0.0 ou ultérieure. Pour effectuer la mise à jour vers la dernière version :

    gcloud components update
    
  2. Pour chaque fichier que vous souhaitez migrer, spécifiez l'une des sous-commandes suivantes (cron-xml-to-yaml, datastore-indexes-xml-to-yaml, dispatch-xml-to-yaml, queue-xml-to-yaml) et le nom du fichier :

    gcloud beta app migrate-config queue-xml-to-yaml MY-QUEUE-XML-FILE.xml
    
  3. Revérifiez manuellement le fichier converti avant de le déployer en production.

    Pour voir un exemple de conversion réussie de fichier xml en yaml, consultez les onglets de la section Migrer manuellement des fichiers.

Migrer manuellement des fichiers

Pour migrer manuellement des fichiers xml vers le format yaml :

cron.yaml

Créez un fichier cron.yaml avec un objet cron contenant une liste d'objets, chacun avec des champs correspondant à chacun des attributs de tag <cron> de votre fichier cron.xml, comme indiqué ci-dessous.

Fichier cron.yaml converti :

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'

Fichier cron.xml d'origine :

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

Pour plus d'informations, consultez la documentation de référence de cron.yaml.

dispatch.yaml

Créez un fichier dispatch.yaml avec un objet dispatch contenant une liste d'objets, chacun avec des champs correspondant à chacun des attributs de tag <dispatch> de votre fichier dispatch.xml, comme indiqué ci-dessous.

Fichier dispatch.yaml converti :

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

Fichier dispatch.xml d'origine

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

Pour plus d'informations, consultez la documentation de référence de dispatch.yaml.

index.yaml

Créez un fichier index.yaml avec un objet indexes contenant une liste d'objets, chacun avec des champs correspondant à chacun des attributs de tag <datastore-index> de votre fichier datastore-indexes.xml, comme indiqué ci-dessous.

Fichier index.yaml converti :

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

Fichier datastore-index.xml d'origine :

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

Pour plus d'informations, consultez la documentation de référence de index.yaml.

queue.yaml

Créez un fichier queue.yaml avec un objet queue contenant une liste d'objets, chacun avec des champs correspondant à chacun des attributs de tag <queue> de votre fichier queue.xml, comme indiqué ci-dessous.

Fichier queue.yaml converti :

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

Fichier queue.xml d'origine :

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