Erste Schritte: Aufgabenwarteschlangen

Nachfolgend werden Aufgabenwarteschlangen beschrieben und es wird gezeigt, wie Sie die App Engine Image API zur Größenänderung von Bildern verwenden.

Aufgabenwarteschlangen führen Code außerhalb direkter Nutzerinteraktionen aus, also im Hintergrund. In diesem Leitfaden wird eine Aufgabenwarteschlange verwendet, um Aufgaben auszuführen, nachdem ein Bild zu Cloud Storage hinzugefügt wurde. Diese sind:

  1. Das soeben in Cloud Storage gespeicherte Bild wird abgerufen.
  2. Das Bild wird mithilfe der Image API in eine Miniaturansicht umgewandelt.
  3. Die Miniaturansicht wird in Cloud Storage gespeichert.

Die App Engine Java 8-Laufzeit unterstützt auch native Java-Bildmanipulierungsklassen wie AWT und Java2D.

Vorbereitung

  1. Konfigurieren Sie Ihre Entwicklungsumgebung und erstellen Sie Ihr App Engine-Projekt.

  2. In dieser Anleitung wird die Apache Commons IOUtils-Bibliothek verwendet. So fügen Sie Ihrem App Engine-Projekt die IOUtils-Bibliothek hinzu:

    Zum pom.xml hinzufügen:

    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.5</version>
    </dependency>
    

Bibliotheken importieren

Im Beispielcode werden die folgenden Importe verwendet:

import com.google.appengine.api.images.Image;
import com.google.appengine.api.images.ImagesService;
import com.google.appengine.api.images.ImagesServiceFactory;
import com.google.appengine.api.images.Transform;
import org.apache.commons.io.IOUtils;

Eine Aufgabenwarteschlange erstellen

App Engine stellt eine default-Aufgabenwarteschlange bereit. Sie können verschiedene Aufgabenwarteschlangen für unterschiedliche Aufgabentypen erstellen. Sie können beispielsweise eine Aufgabenwarteschlange erstellen, um die Größe von Bildern zu ändern, und eine weitere, um die Datenbank Ihrer Anwendung zu aktualisieren.

Erstellen Sie zum Hinzufügen von Warteschlangen die Datei queue.xml im Verzeichnis WEB-INF Ihres App Engine-Projekts. Für eine Aufgabenwarteschlange müssen ein Name und eine Ausführungsrate angegeben werden:

<?xml version="1.0" encoding="UTF-8"?>
  <queue-entries>
    <queue>
      <name>resize-image</name>
      <rate>60/h</rate>
    </queue>
  </queue-entries>

Diese Beispielwarteschlange mit dem Namen resize-image definiert eine Ausführungsrate von 60-mal pro Stunde oder einmal pro Minute. Die vollständigen Optionen für die Listenwarteschlange finden Sie in der Referenz queue.xml.

Eine Aufgabenwarteschlange hat zwei Komponenten: einen Aufgaben-Requester und einen Aufgaben-Handler. Der Requestor fügt eine Aufgabe zur Warteschlange hinzu und sendet sie an den Aufgaben-Handler.

Aufgaben zu einer Warteschlange hinzufügen

So fügen Sie einer Warteschlange eine Aufgabe hinzu:

  1. Erstellen Sie mithilfe von QueueFactory.getQueue() ein Aufgabenwarteschlangenobjekt und stellen Sie dabei sicher, dass Sie den in queue.xml definierten Warteschlangennamen angeben:

    Queue imageResizeQueue; // Taskqueue queue
    
    @Override
    public void init() throws ServletException {
    
      // Setup Cloud Storage service
      gcsService =
          GcsServiceFactory.createGcsService(
              new RetryParams.Builder()
                  .initialRetryDelayMillis(10)
                  .retryMaxAttempts(10)
                  .totalRetryPeriodMillis(15000)
                  .build());
    
      // Initialize the queue object with the specific queue
      imageResizeQueue = QueueFactory.getQueue([QUEUE-NAME]);
    
      // Cloud SQL connection setup
      try {
        final String url = System.getProperty("cloudsql"); // Cloud SQL server URI
    
        try {
          conn = DriverManager.getConnection(url); // Connect to the database
    
          Statement createTable; // SQL statement
    
          // Batch SQL table creation commands
          createTable.addBatch(createContentTableSql);
          createTable.addBatch(createUserTableSql);
          createTable.addBatch(createImageTableSql);
          createTable.addBatch(createBlogPostImageTableSql);
          conn.createTable.executeBatch(); // Execute batch
    
        } catch (SQLException e) {
          throw new ServletException("Unable to connect to Cloud SQL", e);
        }
    
      } finally {
        // Nothing really to do here.
      }
    
    }
    
  2. Fügen Sie dem Objekt Queue Aufgaben hinzu. Wie im Codebeispiel gezeigt, fügt imageResizeQueue.add() dem imageResizeQueue - Objekt eine Aufgabe hinzu:

    try {
      // Add a queued task to create a thumbnail of the uploaded image
      imageResizeQueue.add(
          TaskOptions.Builder.withUrl("/tasks/imageresize").param("filename", filename));
    }
    

    Geben Sie den URI des Aufgaben-Handlers zusammen mit und allen an den Handler gesendeten Parametern mit TaskOptions.Builder.withUrl() an.

    In diesem Beispiel lautet der URI /tasks/imageresize und der Parameter eine Variable namens filename, die den Dateinamen des zu verarbeitenden Bilds enthält.

Einen Aufgaben-Handler erstellen

Sobald Sie der Warteschlange eine Aufgabe hinzugefügt haben, wird der Aufgaben-Handler ausgeführt, der dem URI /tasks/imageresize zugeordnet ist. Ein Aufgaben-Handler ist ein Java-Servlet, das eine Aufgabe so lange ausführt, bis diese erfolgreich abgeschlossen wurde.

In diesem Beispiel hat der Handler drei Aufgaben:

  • das vom Aufrufer angegebene Bild aus Cloud Storage abrufen,

  • das Bild mithilfe der App Engine Image API umwandeln, in diesem Fall in eine Miniaturansicht und

  • das neue Bild (Miniaturansicht) in Cloud Storage speichern.

So erstellen Sie den Aufgaben-Handler:

  1. Fügen Sie eine Anmerkung hinzu, die den Handler dem URI /tasks/imageresize zuordnet:

     @WebServlet(name = "imageResize", description = "Task queue handler", urlPatterns = "/tasks/imageresize")
     public class imageResize extends HttpServlet {
    
       // Task handler functionality
    
     }
    
  2. Richten Sie eine Verbindung zu Cloud Storage ein. Informationen dazu entnehmen Sie der Cloud Storage-Anleitung. Rufen Sie das Bild von Cloud Storage ab:

     public void init() throws ServletException {
    
      // initiate GcsService
      GcsService gcsService =
        GcsServiceFactory.createGcsService(
            new RetryParams.Builder()
                .initialRetryDelayMillis(10)
                .retryMaxAttempts(10)
                .totalRetryPeriodMillis(15000)
                .build());
    }
    
  3. Verarbeiten Sie den eingehenden Aufgabenwarteschlangen-Request und verwenden Sie den angegebenen Dateinamen, um das Bild von Cloud Storage zu laden:

    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    
      String filename = req.getParameter("filename"); // Get the filename passed from the task requestor
      GcsFilename gcsFile = new GcsFilename(bucket, filename); // Create a valid Cloud Storage filename
    
      GcsInputChannel readChannel = gcsService.openPrefetchingReadChannel(gcsFile, 0, BUFFER_SIZE); // Read the file from Cloud Storage
    
  4. Verwenden Sie das Objekt ImagesService, um die Bildgröße anzupassen:

    // Get an instance of the ImagesService we can use to transform images.
    ImagesService imagesService = ImagesServiceFactory.getImagesService();
    
    // Make an image directly from a byte array, and transform it.
    Image image =
        ImagesServiceFactory.makeImage(IOUtils.toByteArray(Channels.newInputStream(readChannel)));
    Transform resize = ImagesServiceFactory.makeResize(100, 50); // resize image to 100x50
    Image resizedImage = imagesService.applyTransform(resize, image);
    
    // Write the transformed image back to a Cloud Storage object.
    gcsService.createOrReplace(
        new GcsFilename(bucket, "thumbnail_" + filename),
        new GcsFileOptions.Builder().acl("public-read").build(),
        ByteBuffer.wrap(resizedImage.getImageData()));
    

    Im obigen Snippet wird die Image API-Methode makeResize() verwendet, um die Größe des Bildes in eine Miniaturansicht zu ändern. Dazu liest er das Bild aus Cloud Storage in ein InputChannel und konvertiert es mit IOUtils.toByteArray() in ein ByteArray.

    Nach dem Anwenden der Transformation wird das neue Bild mit dem String thumbnail_ an seinen Dateinamen angehängt. Die Berechtigung ist öffentlich lesbar und wird in Cloud Storage geschrieben.

URLs von Aufgaben-Handlern absichern

Aufgaben, die vertrauliche Operationen vornehmen, wie zum Beispiel Daten ändern, sollten Sie so absichern, dass sie von externen Nutzern nicht direkt aufgerufen werden können. Beschränken Sie dazu die Berechtigung zum Aufgabenzugriff auf App Engine-Administratoren. Normale Nutzer können dann nicht auf die Aufgaben-URLs zugreifen. Beachten Sie, dass diese Einschränkung nicht für Aufgaben-Requests gilt, die von Ihrer App Engine-App stammen.

Im aktuellen Beispiel haben die Aufgaben-Handler URLs im Ordner /tasks/. Um den Zugriff auf den Ordner /tasks/ auf App Engine-Administratoren zu beschränken, fügen Sie dem web.xml des Projekts Folgendes hinzu:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>tasks</web-resource-name>
        <url-pattern>/tasks/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
    </auth-constraint>
</security-constraint>

Eine einzelne Aufgabe aus einer Warteschlange entfernen

Verwenden Sie deleteTask(), um eine einzelne Aufgabe aus einer Warteschlange zu entfernen:

private void removeTask(Queue queue, String taskName) {
  queue.deleteTask(taskName); // remove a task from a queue
}

Alle Aufgaben aus einer Warteschlange entfernen

Wenn Sie alle Aufgaben aus einer Warteschlange entfernen möchten, verwenden Sie purge(). Es kann bis zu eine Minute dauern, bis alle Aufgaben einer Warteschlange gelöscht sind.

private void purgeQueue(Queue queue) {
  queue.purge(); // remove all tasks from a queue
}

Der Löschvorgang dauert eine Weile. Warten Sie daher einige Sekunden, bevor Sie neue Aufgaben zur Warteschlange hinzufügen.

Eine Aufgabenwarteschlange löschen

Wenn Sie eine Aufgabenwarteschlange löschen möchten, entfernen Sie den Eintrag aus der Datei queue.xml des Projekts und stellen Sie die Datei noch einmal bereit.

In App Engine bereitstellen

Sie können Ihre Anwendung mithilfe von Maven in App Engine bereitstellen.

Wechseln Sie zum Stammverzeichnis Ihres Projekts und geben Sie Folgendes ein:

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

Ersetzen Sie PROJECT_ID durch die ID Ihres Google Cloud-Projekts. Wenn in der Datei pom.xml bereits Ihre Projekt-ID angegeben ist, müssen Sie das Attribut -Dapp.deploy.projectId nicht in dem von Ihnen ausgeführten Befehl einfügen.

Nachdem Ihre Anwendung von Maven bereitgestellt wurde, sollte automatisch ein Webbrowser-Tab in Ihrer neuen Anwendung geöffnet werden. Geben Sie dazu Folgendes ein:

gcloud app browse

Weitere Informationen

In dieser Anleitung wird beschrieben, wie eine Aufgabenwarteschlange verwendet wird, um von einem Bild eine Miniaturansicht zu erstellen und in Cloud Storage zu speichern. Dieselbe Vorgehensweise gilt auch für andere Speicherdienste wie Cloud Datastore oder Cloud SQL.