Migra tu app 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 mediante 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 fundamentales 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 en paquetes, como Memcache y listas de tareas en cola. En su lugar, Google Cloud proporciona productos independientes que son equivalentes a la mayoría de los servicios que se empaquetaban con el entorno de ejecución de Java 8. Cada uno de estos productos de Google Cloud proporciona bibliotecas cliente de Cloud para Java. Para los servicios en paquetes 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 en paquetes 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 de XML. Por ejemplo, puedes especificar la configuración de la app en el archivo app.yaml en lugar de appengine-web.xml. Debes convertir el archivo appengine-web.xml de forma manual al archivo app.yaml. Para obtener más información, consulta Crea un archivo app.yaml.

    Si deseas obtener información para convertir otros tipos de archivos de configuración de App Engine a YAML, consulta Migra formatos de archivo XML a YAML.

  • El entorno de ejecución de Java 11 puede ejecutar cualquier framework de Java, siempre que empaquetes un servidor web configurado para responder a las solicitudes HTTP en el puerto que especifica la variable de entorno PORT (recomendado) o en el puerto 8080. Por ejemplo, el entorno de ejecución de Java 11 puede ejecutar un UberJAR de Spring Boot tal 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 de 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 instancias nuevas de tu app.

Con el comando gcloud app deploy, se 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 del 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 con tus artefactos JAR y este archivo app.yaml, listo para la implementación.

A continuación, se muestra una estructura del proyecto de Maven de muestra:

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

En las siguientes instrucciones, se muestra cómo volver a empaquetar una aplicación hello-world de 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 Jetty simple que carga un archivo WAR y empaqueta tu app en un archivo JAR ejecutable:

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

    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 pasar el archivo WAR como argumento. Por ejemplo, consulta el archivo app.yaml de la muestra helloworld-servlet:

    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 helloworld-servlet si ejecutas el siguiente comando desde la 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, ejecuta el siguiente comando:

    Herramientas de gcloud

    gcloud app deploy

    Complemento de Maven

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

    Reemplaza PROJECT_ID por el ID del 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 frameworks 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 puede que tengas que realizar en tu app 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ía de las funcionalidades que proporcionan las API específicas de App Engine. Para obtener más información, consulta las alternativas recomendadas que se mencionan a continuación.

Blobstore

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

Datastore

Para obtener 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 usarán en gran medida las apps de App Engine.

Imágenes

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 de las imágenes, convertirlas y manipularlas, usa una biblioteca de procesamiento de imágenes, como ImageJ2, imgscalr o thumbnailator. A fin de usar una de estas bibliotecas de terceros, agrega la biblioteca como 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 a través de una URL de entrega. Si deseas una funcionalidad similar, puedes generar las imágenes con cambio de tamaño 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

Recomendamos que actualices tu app para usar Cloud Logging, que admite funciones como ver registros en el visor de registros, descargar registros, filtrar mensajes por gravedad y correlacionar los mensajes de apps con solicitudes específicas. Como alternativa, puedes habilitar estas funciones si escribes mensajes de registro que contengan datos específicos estructurados en un objeto JSON.

Para obtener más información, consulta Escribe y visualiza registros.

Correo electrónico

Para enviar un correo electrónico, 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 la API de Administrador de App Engine junto con una combinación de variables de entorno:

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ó a las apps multiusuario a dividir los datos entre los usuarios con solo especificar 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 aisladas por completo, puedes crear proyectos nuevos de manera programática mediante 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 ella desde tu servicio.

Lista de tareas en cola

Pon en cola tareas para la ejecución de código asíncrono mediante 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 desde 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 se extraen y procesan con 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 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 demuestran cómo migrar tus archivos xml a archivos yaml.

Migra tus archivos de forma automática

Para migrar tus archivos xml de forma automática, 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 subcomandos (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 de un archivo xml a un archivo yaml, consulta las pestañas Migra los archivos de forma manual.

Migra tus archivos de forma manual

Para migrar de forma manual tus archivos xml a archivos yaml, haz lo siguiente:

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>