Migra tu aplicación de App Engine de Java 8 a Java 11

Además de admitir el entorno de ejecución de Java 8, App Engine ahora admite el alojamiento de tus aplicaciones con el entorno de ejecución de Java 11. Aprende a migrar tu app de App Engine existente de Java 8 a Java 11 en el entorno estándar.

Diferencias clave entre los entornos de ejecución de Java 8 y Java 11

  • A partir del entorno de ejecución de Java 11, el entorno estándar de App Engine ya no incluye servicios de App Engine agrupados, como Memcache y las listas de tareas en cola. En su lugar, Google Cloud proporciona productos independientes que son equivalentes a la mayoría de los servicios agrupados en el entorno de ejecución de Java 11. Cada uno de estos productos de Google Cloud proporciona bibliotecas cliente de Cloud para Java. Para los servicios agrupados que no están disponibles como productos separados en Google Cloud, como procesamiento de imágenes, búsqueda y mensajería, puedes usar proveedores de terceros o algunas otras soluciones, como se sugiere en esta guía de migración.

    Quitar los servicios de App Engine agrupados permite que el entorno de ejecución de Java 11 admita una experiencia de desarrollo de Java completamente idiomática. En el entorno de ejecución de Java 11, escribes una app estándar de Java que es completamente portátil y puede ejecutarse en cualquier entorno estándar de Java, incluido App Engine.

  • En el entorno de ejecución de Java 11, los archivos de configuración están en formato YAML en lugar del formato XML. Por ejemplo, debes especificar la configuración de tu aplicación en el archivo app.yaml, en lugar del archivo appengine-web.xml. Debes convertir el archivo appengine-web.xml a app.yaml de forma manual. Para obtener más información, consulta Crea un archivo app.yaml.

    Para obtener información sobre cómo convertir otros tipos de archivos de configuración de App Engine a YAML, consulta Migra XML a formatos de archivo YAML.

  • El entorno de ejecución de Java 11 puede ejecutar cualquier marco de trabajo de Java siempre que empaques un servidor web configurado para responder a las solicitudes HTTP en el puerto especificado por la variable de entorno PORT (recomendado) o en el puerto 8080. Por ejemplo, el entorno de ejecución de Java 11 puede ejecutar un Spring Boot JAR como está.

Crea un archivo app.yaml

En el entorno de ejecución de Java 11, la configuración de la aplicación se almacena en el archivo app.yaml. Debes quitar el archivo appengine-web.xml de tu aplicación existente y reemplazarlo por un archivo app.yaml.

El único elemento obligatorio en un archivo app.yaml es el elemento runtime:

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

App Engine proporciona valores predeterminados para todas las demás opciones de configuración, incluida la clase de instancia F1, que determina la memoria y los recursos de CPU disponibles para tu app, y el ajuste de escala automático, que controla cómo y cuándo se crean las nuevas instancias de tu app.

El comando gcloud app deploy puede crear un archivo app.yaml cuando implementas tu app. El archivo app.yaml que crea App Engine contiene solo la entrada runtime y tu app usará valores predeterminados para todas las opciones de configuración.

Si necesitas anular la configuración predeterminada, crea un archivo app.yaml y especifica la configuración que necesitas. Para obtener más información, consulta la referencia de un archivo app.yaml.

Para un proyecto de Maven, la ubicación estándar del archivo app.yaml se encuentra en el directorio src/main/appengine. El complemento de Maven de App Engine creará un directorio target/appengine-staging correcto que contenga tus artefactos JAR y este archivo app.yaml, listos para la implementación.

A continuación, se muestra un ejemplo de la estructura del proyecto de Maven:

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

Si tienes más de un archivo JAR en el directorio de tu proyecto o deseas especificar un punto de entrada personalizado, debes especificarlo en el elemento entrypoint de tu archivo app.yaml.

Vuelve a empaquetar un archivo WAR en un archivo JAR

Para migrar al entorno de ejecución de Java 11, debes volver a empaquetar tu aplicación web de Java 8 de App Engine en un archivo JAR ejecutable.

Tu aplicación debe tener una clase Main que inicie un servidor web que responda a las solicitudes HTTP en el puerto especificado por la variable de entorno PORT, por lo general, 8081.

Por ejemplo:

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();
  }
}

Ejemplo de migración de WAR

Las siguientes instrucciones muestran cómo volver a empaquetar una aplicación hello-worldde Java 8 de App Engine como un JAR para que se ejecute en el entorno de ejecución de Java 11.

La migración usa el artefacto appengine-simple-jetty-main. Esto proporciona una clase Main con un servidor web simple de Jetty que carga un archivo WAR y empaqueta tu aplicación en un archivo JAR ejecutable:

  1. Clona en tu máquina local el artefacto del servidor Jetty incorporado:

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

    De manera opcional, puedes descargar la muestra como un archivo zip y extraerla.

  2. Ve al directorio que contiene el código de muestra:

    cd java-docs-samples/appengine-java11/appengine-simple-jetty-main/
    
  3. Instala la dependencia de forma local:

    mvn install
    
  4. Agrega el siguiente código al archivo pom.xml de tu proyecto:

    • dependencia appengine-simple-jetty-main:
      <dependency>
        <groupId>com.example.appengine.demo</groupId>
        <artifactId>simple-jetty-main</artifactId>
        <version>1</version>
        <scope>provided</scope>
      </dependency>
    • Complemento 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 implementa archivos ubicados en el directorio ${build.directory}/appengine-staging. Si agregas el complemento maven-dependency a tu compilación, App Engine instala las dependencias especificadas en la carpeta correcta.
  5. Crea un elemento entrypoint en el archivo app.yaml para llamar al objeto appengine-simple-jetty-main y pasa el archivo WAR como argumento. Por ejemplo, consulta el archivo muestra de helloworld-servlet app.yaml:

    runtime: java11
    entrypoint: 'java -cp "*" com.example.appengine.demo.jettymain.Main helloworld.war'
  6. Para ejecutar tu aplicación de forma local, sigue estos pasos:

    1. Empaqueta tu aplicación:

      mvn clean package
      
    2. Inicia el servidor con tu archivo WAR como argumento.

      Por ejemplo, puedes iniciar el servidor en la muestra de helloworld-servlet si ejecutas el siguiente comando desde tu carpeta java-docs-samples/appengine-java11/appengine-simple-jetty-main/:

      mvn exec:java -Dexec.args="../helloworld-java8/target/helloworld.war"
      
    3. En el navegador web, ingresa la siguiente dirección:

      http://localhost:8080

  7. Para implementar tu aplicación:

    Herramientas de gcloud

    gcloud app deploy

    Complemento de Maven

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

    Reemplaza PROJECT_ID con el ID de tu proyecto de Cloud. Si tu archivo pom.xml ya especifica tu ID del proyecto, no necesitas incluir la propiedad -Dapp.deploy.projectId en el comando que ejecutas.

Flexibilidad del framework

El entorno de ejecución de Java 11 no incluye ningún framework de entrega web, lo que significa que ya no estás limitado a los marcos de trabajo basados en servlet.

Hay muestras de hello world que usan frameworks web populares de Java en el repositorio de GitHub de Google Cloud:

Migra desde el SDK de Java de App Engine

El entorno de ejecución de Java 11 no es compatible con el SDK de Java en App Engine. A continuación, se enumeran algunos cambios que puedes tener que realizar en tu aplicación existente de App Engine para Java 8 y tu proceso de implementación a fin de usar el entorno de ejecución de App Engine Java 11:

Migra desde las API específicas de App Engine

La Biblioteca cliente de Google Cloud para Java ahora proporciona la mayor parte de la funcionalidad de las API específicas de App Engine Para obtener más información, consulta las alternativas recomendadas que se enumeran a continuación.

Blobstore

Para almacenar y recuperar datos, usa Cloud Storage a través de las bibliotecas cliente de Cloud. Para comenzar, consulta Uso de Cloud Storage.

Datastore

Para una alternativa a una base de datos de clave-valor de NoSQL como la API de Datastore, usa Cloud Firestore en modo Datastore. Firestore es la versión más reciente de Datastore y se recomienda el modo Datastore para las bases de datos que serán usadas principalmente por las aplicaciones de App Engine.

Images

Puedes entregar imágenes desde Cloud Storage, entregarlas directamente o usar una red de distribución de contenidos (CDN) de terceros.

Para cambiar el tamaño, convertir y manipular imágenes, usa una biblioteca de procesamiento de imágenes como ImageJ2, imgscalr o miniatura. Para usar una de estas bibliotecas de terceros, agrega la biblioteca como una dependencia y actualiza tu código para llamar a las API de la biblioteca.

El servicio de imágenes de App Engine también proporcionó la funcionalidad para evitar solicitudes dinámicas a tu aplicación mediante el manejo del cambio de tamaño de la imagen mediante una URL de entrega. Si deseas una funcionalidad similar, puedes generar las imágenes de tamaño nuevo por adelantado y subirlas a Cloud Storage para entregarlas. De forma alternativa, puedes usar un servicio de red de distribución de contenidos (CDN) de terceros que permita cambiar el tamaño de las imágenes.

Logging

Te recomendamos que actualices tu app para usar Cloud Logging, que admite las mismas funciones que App Engine Logging, como filtrar mensajes que tu app escribe por gravedad y correlacionar esos mensajes con solicitudes específicas. Como alternativa, puedes habilitar el filtrado y la correlación si escribes mensajes de registro que contengan datos específicos estructurados en un objeto JSON.

Para obtener más información, consulta Cómo escribir y ver registros.

Correo electrónico

Para enviar correos electrónicos, usa un proveedor de correo electrónico de terceros, como SendGrid, Mailgun o Mailjet. Todos estos servicios ofrecen API para enviar correos electrónicos desde aplicaciones.

Memcache

A fin de almacenar en caché los datos de la aplicación, usa Memorystore para Redis.

Módulos

Para obtener información y modificar los servicios en ejecución de tu aplicación, usa una combinación de variables de entorno y la API de Administrador de App Engine:

Información del servicio Cómo acceder
ID de aplicación actual Con la variable de entorno GAE_APPLICATION.
ID del proyecto actual Con la variable de entorno GOOGLE_CLOUD_PROJECT.
Nombre del servicio actual Con la variable de entorno GAE_SERVICE.
Versión del servicio actual Con la variable de entorno GAE_VERSION.
ID de instancia actual Con la variable de entorno GAE_INSTANCE.
Nombre de host predeterminado Método apps.get de la API de Administrador
Lista de servicios Método apps.services.list de la API de Administrador
Lista de versiones para un servicio Método apps.services.versions.list de la API de Administrador
Versión predeterminada para un servicio, incluidas las divisiones del tráfico Método apps.services.get de la API de Administrador
Lista de instancias en ejecución para una versión Método apps.services.versions.instances.list de la API de Administrador

Para obtener más información acerca de los datos disponibles sobre los servicios en ejecución de tu aplicación, consulta Entorno de ejecución de Java 11.

Espacios de nombres

La API de espacios de nombres habilitó las aplicaciones multiusuario para dividir los datos entre los usuarios simplemente mediante la especificación de una string de espacio de nombres única para cada usuario.

Si bien Datastore admite multiusuario directamente, otros servicios de Google Cloud no lo hacen. Si tu app multiusuario usa otros servicios de Google Cloud, deberás administrar la función multiusuario de forma manual. Para tener instancias de servicios totalmente aisladas, puedes crear proyectos nuevos de manera programática con la API de Cloud Resource Manager y acceder a los recursos de todos los proyectos.

OAuth

En lugar de usar el servicio de OAuth de App Engine para verificar los tokens de OAuth 2.0, usa el método oauth2.tokeninfo de la API de OAuth 2.0.

Aloja cualquier base de datos de búsqueda en el texto completo como ElasticSearch en Compute Engine y accede a esta desde tu servicio.

Lista de tareas en cola

Pon en cola tareas para la ejecución de código asíncrono con la API de REST de Cloud Tasks, la API de RPC o la biblioteca cliente de Google Cloud, y usa un servicio estándar de App Engine de Java 11 como objetivo Push. Para obtener más información, consulta Migra de listas de tareas en cola a Cloud Tasks.

En muchos casos en los que puedes usar listas de extracción, como poner en cola tareas o mensajes que serán extraídos y procesados por trabajadores separados, Pub/Sub puede ser una buena alternativa, ya que ofrece funcionalidades y garantías de entrega similares. Consulta el siguiente programa de muestra que interactúa con la API de Cloud Tasks.

Autenticación de usuarios

Para una alternativa a la API de usuarios, usa cualquier mecanismo de autenticación basado en HTTP como los que se mencionan a continuación:

Migra formatos de archivo de XML a YAML

El SDK de Cloud no admite los siguientes formatos de archivo:

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

Los siguientes ejemplos muestran cómo migrar tus archivos xml a archivos yaml.

Migra tus archivos automáticamente

Para migrar tus archivos xml automáticamente, sigue estos pasos:

  1. Debes tener la versión 226.0.0 del SDK de Cloud o posterior. Para actualizar a la última versión, haz lo siguiente:

    gcloud components update
    
  2. Para cada archivo que desees migrar, especifica uno de los siguientes comandos secundarios (cron-xml-to-yaml, datastore-indexes-xml-to-yaml, dispatch-xml-to-yaml, queue-xml-to-yaml) y el nombre del archivo:

    gcloud beta app migrate-config queue-xml-to-yaml MY-QUEUE-XML-FILE.xml
    
  3. Realiza una doble comprobación del archivo convertido antes de realizar la implementación en producción.

    Para obtener una conversión de muestra exitosa xml al archivo yaml, consulta las pestañas Migra los archivos de forma manual.

Migra tus archivos manualmente

Para migrar manualmente tus archivos xml a archivos yaml:

cron.yaml

Crea un archivo cron.yaml con un objeto cron que contenga una lista de objetos, cada uno con campos que correspondan a cada uno de los atributos de la etiqueta <cron> en tu archivo cron.xml, como se muestra a continuación.

Archivo cron.yaml convertido:

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'

Archivo cron.xml original:

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

Para obtener más información, consulta la documentación de referencia de cron.yaml.

dispatch.yaml

Crea un archivo dispatch.yaml con un objeto dispatch que contenga una lista de objetos, cada uno con campos que correspondan a cada uno de los atributos de la etiqueta <dispatch> en tu archivo dispatch.xml, como se muestra a continuación.

Archivo dispatch.yaml convertido:

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

Archivo dispatch.xml original

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

Para obtener más información, consulta la documentación de referencia de dispatch.yaml.

index.yaml

Crea un archivo index.yaml con un objeto indexes que contenga una lista de objetos, cada uno con campos que correspondan a cada uno de los atributos de la etiqueta <datastore-index> en tu archivo datastore-indexes.xml, como se muestra a continuación.

Archivo index.yaml convertido:

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

Archivo datastore-index.xml original:

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

Para obtener más información, consulta la documentación de referencia de index.yaml.

queue.yaml

Crea un archivo queue.yaml con un objeto queue que contenga una lista de objetos, cada uno con campos que correspondan a cada uno de los atributos de la etiqueta <queue> en tu archivo queue.xml, como se muestra a continuación.

Archivo queue.yaml convertido:

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

Archivo queue.xml original:

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