使用入门:任务队列

了解如何使用任务队列和 App Engine Image API 来调整图片大小。

任务队列无需用户直接交互即可执行代码,同时支持任务在后台运行。在为 Cloud Storage 添加图片后,本指南使用任务队列执行任务。 任务队列中要执行的任务如下:

  1. 检索刚刚上传到 Cloud Storage 的图片文件。
  2. 使用 Image API 将其调整为缩略图。
  3. 将生成的缩略图存储在 Cloud Storage 中。

App Engine Java 8 运行时还支持 Java 的原生图片处理类,例如 AWTJava2D

准备工作

  1. 配置开发环境并创建 App Engine 项目

  2. 本指南使用的是 Apache Commons IOUtils 库。要将 IOUtils 库添加到您的 App Engine 项目中,请执行以下操作:

    添加到您的 pom.xml

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

导入库

本指南中提供的示例代码使用以下导入项:

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;

创建任务队列

App Engine 提供了 default 任务队列,但您可以为不同类型的工作创建不同的任务队列。例如,您可以创建一个任务队列来调整图片大小,另外创建一个任务队列来更新应用的数据库。

如需添加队列,请在 App Engine 项目的 WEB-INF 目录中创建 queue.xml 文件。任务队列必须指定名称和执行速率:

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

上述名为 resize-image 的示例队列将执行速率指定为每小时 60 次,即每分钟一次。如需查看完整的队列选项列表,请参阅 queue.xml 参考文档

任务队列由两部分组成:任务请求程序和任务处理程序。请求程序用于将任务添加到队列并将其发送给任务处理程序。

将任务添加到队列

要将任务添加到队列,请执行以下操作:

  1. 使用 QueueFactory.getQueue() 创建任务队列对象,并确保指定 queue.xml 中定义的队列名称:

    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. 将任务添加到 Queue 对象。如代码示例所示,imageResizeQueue.add() 将任务添加到 imageResizeQueue 对象:

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

    使用 TaskOptions.Builder.withUrl() 指定任务处理程序的 URI,并指定发送给处理程序的任何参数。

    在此示例中,URI 为 /tasks/imageresize,而参数是名为 filename 的变量,其中包含待处理图片的文件名。

创建任务处理程序

将任务添加到队列后,映射至 URI /tasks/imageresize 的任务处理程序将运行。任务处理程序是一个尝试执行任务(直到成功执行)的 Java Servlet。

在本示例中,任务处理程序将执行以下三项任务:

  • 从 Cloud Storage 中检索调用程序指定的图片。

  • 使用 App Engine Image API 转换图片,在本示例中是转换为缩略图。

  • 将转换后的图片(缩略图)存储在 Cloud Storage 中。

要创建任务处理程序,请执行以下操作:

  1. 添加将处理程序映射到 URI /tasks/imageresize 的注释:

     @WebServlet(name = "imageResize", description = "Task queue handler", urlPatterns = "/tasks/imageresize")
     public class imageResize extends HttpServlet {
    
       // Task handler functionality
    
     }
    
  2. 按照 Cloud Storage 使用指南中的说明建立与 Cloud Storage 的连接,并从 Cloud Storage 中检索图片:

     public void init() throws ServletException {
    
      // initiate GcsService
      GcsService gcsService =
        GcsServiceFactory.createGcsService(
            new RetryParams.Builder()
                .initialRetryDelayMillis(10)
                .retryMaxAttempts(10)
                .totalRetryPeriodMillis(15000)
                .build());
    }
    
  3. 处理传入的任务队列请求,同时使用提供的文件名从 Cloud Storage 中检索图片:

    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. 使用 ImagesService 对象调整图片大小:

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

    上述代码段使用 Image API makeResize() 方法将图片调整为缩略图。为此,该方法会从 Cloud Storage 中读取图片并将其写入 InputChannel,然后使用 IOUtils.toByteArray() 将其转换为 ByteArray。

    转换完成后,新图片的文件名中将附加字符串 thumbnail_,其权限设置为可公开读取并写入 Cloud Storage。

保护任务处理程序网址

您应保护好用于执行敏感操作的任务,例如通过修改数据来使外部用户无法直接调用它们。为此,您可以设置限制,只允许 App Engine 管理员访问任务,从而阻止用户访问任务网址。请注意,这种限制不适用于来自 App Engine 应用的任务请求。

在本示例中,任务处理程序的网址位于 /tasks/ 文件夹中。 如需限制 App Engine 管理员对 /tasks/ 文件夹的访问,请将以下内容添加到项目的 web.xml

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

从队列中移除单个任务

如需从队列中移除单个任务,请使用 deleteTask()

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

从队列中移除所有任务

如需从队列中移除所有任务,请使用 purge()。完全清除队列中的所有任务最多可能需要一分钟的时间。

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

从队列中移除所有任务可能需要一分钟的时间,因此您应等待几秒钟,之后再向队列中添加新任务。

删除任务队列

如需删除任务队列,请从项目的 queue.xml 文件中移除该条目并重新部署。

部署到 App Engine

您可以使用 Maven 将应用部署到 App Engine。

转到项目的根目录并输入以下命令:

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

PROJECT_ID 替换为您的 Cloud 项目 ID。如果您的 pom.xml 文件已经指定了您的项目 ID,则您无需在运行的命令中添加 -Dapp.deploy.projectId 属性。

Maven 完成应用部署后,输入以下命令可在新应用中自动打开网络浏览器标签:

gcloud app browse

后续步骤

本指南介绍了如何使用任务队列来创建图片缩略图并将其存储在 Cloud Storage 中。该服务可与其他存储服务(如 Cloud DatastoreCloud SQL)结合使用。