65.9K
CodeProject 正在变化。 阅读更多。
Home

Google Cloud Platform:开始使用 Google App Engine

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2013年12月3日

CPOL

13分钟阅读

viewsIcon

28299

这是关于 Google Cloud Platform 系列文章的第一篇,我们将一起学习如何开始使用 Google App Engine(因为大多数新应用程序都始于某种代码)。

第一部分:欢迎回来

高中时,我总是看着“酷小孩俱乐部”的成员们迟到参加派对,隆重登场,吸引所有人的目光,然后又带着我想要搭讪的女孩们离开。我总是早早地到达派对,获得一个好座位,观察各种热闹场面,但有时,如果你去得太早,也没有多少人可以和你聊天。

似乎,技术也是如此:进入新技术的早期参与者有时会发现,大家还没有准备好听他们介绍自己的产品,至少也要等到有更多竞争者加入,并且对这个新事物有更好的理解。这在云计算领域确实如此:2008 年,Google 加入(也许“推出”这个动词更合适)了新兴的云计算行列,发布了“Google App Engine”,但除了“为什么这更好?”的疑问以及其他公司(如 Azure、Heroku、VMWare 等)随后纷纷加入云计算大军掀起的喧嚣之外,很容易就让人忽略了 Google 的产品。

怎么办?就像派对老手们学到的那样:离开,然后再次回来,这次盛装打扮,隆重登场。

随着 Google 在 2012 年发布“Google Cloud Platform”公告,Google App Engine(以及使用它的开发者)不仅迎来了一批新的合作伙伴来辅助云端应用程序开发,还重新将焦点聚集在公司的产品和在世界上最大服务器集群之一上运行代码的机会上。对于那些从未了解过 Google 的 Cloud Platform 实现的人来说,是时候去了解了。对于那些在 2008 年了解过Google App Engine 的人来说,欢迎回来,请继续关注——这将是一个全新的局面。

这是关于 Google Cloud Platform 系列文章的第一篇,我们将一起学习如何开始使用Google App Engine(因为大多数新应用程序都始于某种代码)。之后,在接下来的文章中,我们将探讨 Google Cloud Platform 的更多功能集,包括Google App Engine 提供的一些 API 和工具,以及 Google Cloud Platform 中的数据存储选项。我们不会涵盖所有内容——即使是一本书也很难详细介绍——但我们会涵盖足够的内容,以便您开始学习并知道在需要时到哪里去寻找更多信息。

概述

Google Cloud Platform 由几个组件组成,它们结合起来形成了一个全面的应用程序开发平台。

Compute

Google App Engine(PaaS)和Google Compute Engine(IaaS)是服务器端处理组件。Compute Engine 允许您在云中运行 Linux 虚拟机(类似于 Amazon 的 EC2)。在这两者中,我们将重点关注Google App Engine,这是更传统的方法:一个应用程序托管平台,负责为您处理服务器。对于 Google App Engine,开发者可以使用四种语言中的一种编写代码,通常是 Python 或 Java。Google App Engine 还支持 PHP 和 Go。在本系列文章和其他文章中,我们将使用 Java;这不应被视为技术判断——对于那些偏爱动态脚本语言而非静态类型编译语言的人来说,Python 是一个同样可行的选择。

话虽如此,但请记住,Google App Engine 运行 Java 字节码(并且不在乎字节码的来源),因此那些希望使用 Scala、Clojure 或任何其他基于 JVM 的语言编写代码的开发者非常受欢迎,只要他们能够将其编译成 .class 文件并打包成 JavaEE WAR 格式。(Google 不直接支持 Java 以外的任何语言,但正如我们稍后将看到的,如果它可以在本地机器上运行,那么它应该可以在 Google Cloud Platform 上运行,所以这并不像听起来那么可怕。)

存储

大多数云应用程序都需要存储数据,Google Cloud Platform 提供了三种存储选项:Google Cloud Datastore(NoSQL,非关系型数据库)、Google Cloud SQL(MySQL,关系型数据库)和Google Cloud Storage(对象/Blob 存储)。在这三种选项中,鉴于大多数 Web 开发者都熟悉关系型数据库,使用Google Cloud SQL 以关系型格式存储数据对他们来说是一个相对直接的选择。

然而,并非所有事物都适合关系型格式,与其费力地将不真正遵循关系模型的对象强行纳入其中,不如使用 Google 提供的Google Cloud Storage,它提供了一个“面向存储桶”的模型来存储数据和元数据。这个模型大致类似于 Amazon 的 S3 存储系统,但有足够显著的差异,值得单独讨论。

分析

虽然本系列文章不会涉及,但 Google 还提供大数据选项。Google BigQuery 允许针对 TB 级数据集进行大规模查询。或者,对于那些更偏爱 Hadoop 的人来说,还有Compute Engine 上的 Hadoop

附加功能

Google Cloud Platform 还提供了一些非传统的云选项。其中之一是Google Prediction API,它提供了一些机器学习算法,适用于那些可以从客户反馈分析、垃圾邮件检测或文档分类等中受益的应用程序。另一个是Google Translate API,它提供了语言翻译功能,大大减少了开发者在国际化方面(存储文本到资源文件、根据用户选择的本地化设置获取这些资源的翻译版本等)进行传统工作的需求。

但在此之前,任何炫酷的功能都无法启用,开发者需要开始掌握Google App Engine 的基础知识。

Google App Engine 入门

任何工具或技术的第一个步骤都是在该工具中创建一个“Hello World”应用程序,这里也不例外。它始于选择 Google App Engine 支持的四种语言之一(在此例中,我选择 Java),下载相应的语言工具(如果开发者的机器上还没有,例如 JDK)以及语言特定的 Google Cloud SDK(在此例中是 Java 版本),该 SDK 可在此下载。一旦所有这些工具都就绪,就可以创建 Hello World 应用程序了。尽管部署之前需要设置 Google Cloud Platform 账户,但我们开发应用程序时并不需要它,因为这些工具可以在本地运行,运行一个模拟Google App Engine 行为的本地 Web 服务器。

在开发机器上安装好所有东西后,将Google App Engine SDK 的“bin”目录添加到 PATH 环境变量中,并通过在命令行中输入“appcfg”命令来验证它是否正在运行。这个批处理文件/Shell 脚本是一个命令行工具,它提供了与上传、下载和跟踪应用程序(一旦部署到云中)相关的各种实用程序;目前我们只关心它是否正确安装了 SDK。假设它工作正常,我们就可以开始研究为Google App Engine 开发代码了。

Google App Engine

从根本上说,用 Java 编写Google App Engine 代码,它就是一个 Java Servlet 平台,这意味着 Java 开发者将接触到熟悉且被广泛理解的东西,更不用说可扩展性了。(许多“替代性”的基于 JVM 的语言和 Web 工具包都假定一个基于 Servlet 和相应 Servlet 规范的基线平台,这意味着,绝大多数情况下,它们应该能够正常工作,只需很少甚至无需修改。)考虑到 Servlet 平台的流行度和普遍性,我们不会在这里深入介绍如何编写 Servlet;有大量的资源讨论 Servlet 的细节,包括 Oracle 网站上的教程以及 CodeProject 上的教程。通过将 Servlet 作为其基线平台,顺便说一句,这意味着所有传统的 Java Web 组件都可以用于Google App Engine,例如 Java Server Pages (JSP)。然而,基于 Servlet 的平台也意味着,与一些最近的 Web 工具包希望拥抱 Ruby on Rails 风格的“约定优于配置”的愿望相比,Java 基础的Google App Engine 代码要正确工作,需要有几个不同的配置文件。

此外,在开发Google App Engine 应用程序时,通常会使用几种工具,为了简化这些工具的使用,Google 提供了一个基于 Eclipse 的插件。由于并非所有人都使用 Eclipse 进行 Servlet 开发,因此 Google 还提供了 Ant 集成,并且由于了解“幕后”发生的事情很有帮助,因此我们将使用 Ant 脚本而不是 Eclipse 插件。

遵循所有 Java 开发者的优良传统,即找出基础 Ant 脚本并一遍又一遍地复制粘贴,一个有用的起点是使用图 1 中列出的示例 Ant 脚本。它假定有一个基本的目录结构,包括两个子目录:“src”,其中包含大多数 Java 项目常见的、按子目录组织的包结构;以及“war”,这是应用程序资源和代码的 WAR(Web ARchive)布局。同样,这些在其他地方都有详细描述,但对于那些不记得细节的人来说,WAR 布局大致如图 2 所示。Ant 文件中唯一的其他依赖项是名为“sdk.dir”的属性,该属性必须指向Google App Engine SDK 安装的根目录;在此(略有修改)版本的示例 Ant 脚本中,该值是从环境中提取的,以便允许全局安装 SDK,而不是像示例 Ant 脚本所假定的那样相对于项目目录。

与任何 Ant 脚本一样,运行“ant –projecthelp”将列出所有目标,但有几个目标是显而易见的。

  • “compile”将源代码编译到“war/WEB-INF/classes”中,WAR 格式要求在此处放置已编译的类文件,并将Google App Engine 的 JAR 文件从 SDK 复制到“war/WEB-INF/lib”中,WAR 格式同样要求在此处放置依赖的 JAR 文件。请注意,如果应用程序使用了Google App Engine 之外的任何库,开发者有责任自己将其放入“war/WEB-INF/lib”中;Ant 脚本将假定该目录中的任何 JAR 文件都是编译过程的一部分,并将其添加到编译 CLASSPATH 中。
  • “datanucleusenhance”将获取已编译的代码并对其运行“enhancement”过程,通过自定义 Ant 任务“enhance_war”为其准备数据访问。由于应用程序(尚)不需要任何数据访问,因此此步骤是不必要的,但它需要一个“enhancement descriptor”文件,我们暂时不讨论(或构建)它,所以目前,注释掉此任务的部分,否则我们将遇到不希望立即处理的错误。
  • “runserver”启动一个模拟 Google App Engine 环境的本地 HTTP 服务器,默认使用本地机器的 8080 端口。这是本地/开发 Web 服务器使用的相对流行的端口号,因此如果启动出现错误,请检查是否没有其他服务器正在监听该端口。它还使用“war”子目录作为 Web 服务器的“根”,因此“war”子目录中的任何 HTML 文件都应该可以立即浏览和可见。此任务运行时不会返回,因此“ant runserver”的启动应仅在单独的终端/命令提示符窗口中进行。
  • “update”和“rollback”分别会将应用程序上传和回滚到 Google Cloud Platform,我们目前也还没有准备好——稍后将详细介绍。

除了配置 SDK 位置(并注释掉目前不必要的 enhancement 步骤)之外,Ant 脚本基本上可以使用了。因此,如果我们启动“ant runserver”,即使没有代码,我们也应该在“war”子目录的根目录中看到一个简单的“index.html”文件的内容。

配置 

好吧,差不多可以使用了。Ant 脚本本身已准备就绪,但在 Ant 脚本启动开发 Web 服务器之前,需要准备好两个配置文件。一个是 Servlet 强制要求的“web.xml”文件,它描述了(除其他外)URL 模式与应用程序中 Servlet 的关系;另一个是Google App Engine 强制要求的“appengine-web.xml”文件,它描述了此应用程序的Google App Engine 特定元素。

第一个,“web.xml”,可以通过从最简单的 web.xml 文件开始轻松完成,如图 3 所示。“<welcome-file-list>”元素简单地按顺序描述了服务器在收到请求时将用于服务的“默认”资源的文件的列表。我们稍后将添加的其他元素将是“<servlet>”元素,用于描述一个 Servlet 类并为其指定一个(在此应用程序中)唯一的名称,以及“<servlet-mapping>”,它将采用一个 URL 模式并根据名称将其与 Servlet 关联起来。

(顺便说一句,请注意,“web.xml”文件中这些元素的出现顺序很重要;请参阅 Servlet 规范了解更多详细信息,或使用 XSD 在您喜欢的编辑器或 IDE 中进行代码补全,以避免愚蠢的顺序相关错误和疏漏。)

第二个,“appengine-web.xml”,是Google App Engine 的特定信息,如图 4 所示,目前,文件中唯一关键的元素是“<threadsafe>”元素,它指示应用程序是否允许应用程序内存在多个线程—如果缺少此元素,开发服务器将无法启动。(“<application>”和“<version>”元素在我们将其部署到云时将更为重要,但目前可以保持原样。)“<system-properties>”是将被传递到 JVM 的属性(就像它们是通过命令行使用“-D”传递的一样),允许进行配置,在本例中包括设置诊断日志配置。

这两个文件都必须位于“war/WEB-INF”目录中,并且如果两者都存在,“ant runserver”在命令行中应该能启动“war”目录中“index.html”文件的内容。

展示代码!

哇。大量的设置,我们还没有真正开始编写代码。不过,纠正这个问题相当直接:创建一个简单的 Servlet,如下所示。

package com.tedneward.hello;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloServlet extends HttpServlet
{
  public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
  {
    PrintWriter out = response.getWriter();
    out.println("<html>");
    out.println("<body>");
    out.println("<p>Hello, world</p>");
    out.println("</body>");
    out.println("</html>");
  }
}

然后修改“web.xml”文件,以包含一个命名它的“<servlet>”元素和一个将它与 URL 模式绑定的“<servlet-mapping>”元素。

<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
  <servlet>
    <servlet-name>Hello</servlet-name>
    <servlet-class>com.tedneward.hello.HelloServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Hello</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>

  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
</web-app>

再次执行“ant runserver”,假设代码没有错误,服务器应该会启动。现在,在浏览器中,将“https://:8080/hello”输入到地址栏,应该会显示 Servlet 生成的响应。

不怎么令人印象深刻的演示

就其本身而言,所取得的成就似乎微不足道—事实上,感觉上我们花在编写和部署 Servlet 上的时间比花在 Google Cloud Platform 上的时间还要多。事实上,这也是重点之一:Google App Engine 本质上是一个 Servlet 平台,这意味着 Java 开发者几乎不需要重新学习即可理解Google App Engine 将如何融入开发和部署流程,这与本地的 Tomcat 或其他 JavaEE Servlet 容器不同。

此外,Google App Engine 还有更多值得我们探索的地方—除了Google App Engine 为 Java 开发者提供的库之外,还有数据访问元素,以及一个相当直接(意思是“简单”)的部署到云的步骤。但目前,我们已经拥有一个完全启动并运行的本地开发环境,这意味着 Java 开发者想要做的所有传统事情,例如单元测试和/或端到端测试,仍然可以进行,而无需因云部署步骤而使事情复杂化。

下一篇文章会更精彩,请密切关注。在此期间,祝您编码愉快!

图 1:Google App Engine Ant

<project>
  <property environment="env" />
  <property name="sdk.dir" location="${env.APPENG_HOME}" />

  <import file="${sdk.dir}/config/user/ant-macros.xml" />

  <path id="project.classpath">
    <pathelement path="war/WEB-INF/classes" />
    <fileset dir="war/WEB-INF/lib">
      <include name="**/*.jar" />
    </fileset>
    <fileset dir="${sdk.dir}/lib">
      <include name="shared/**/*.jar" />
    </fileset>
  </path>

  <target name="copyjars"
      description="Copies the Google App Engine JARs to the WAR.">
    <copy
        todir="war/WEB-INF/lib"
        flatten="true">
      <fileset dir="${sdk.dir}/lib/user">
        <include name="**/*.jar" />
      </fileset>
    </copy>
  </target>

  <target name="compile" depends="copyjars"
      description="Compiles Java source and copies other source files to the WAR.">
    <mkdir dir="war/WEB-INF/classes" />
    <copy todir="war/WEB-INF/classes">
      <fileset dir="src">
        <exclude name="**/*.java" />
      </fileset>
    </copy>
    <javac
        srcdir="src"
        destdir="war/WEB-INF/classes"
        classpathref="project.classpath"
        debug="on" />
  </target>

  <target name="datanucleusenhance" depends="compile"
      description="Performs JDO enhancement on compiled data classes.">
    <enhance_war war="war" />
  </target>

  <target name="runserver" depends="datanucleusenhance”>
      description="Starts the development server.">
    <dev_appserver war="war" />
  </target>

  <target name="update" depends="datanucleusenhance"
      description="Uploads the application to Google App Engine.">
    <appcfg action="update" war="war" />
  </target>

  <target name="update_indexes" depends="datanucleusenhance"
      description="Uploads just the datastore index configuration to Google App Engine.">
    <appcfg action="update_indexes" war="war" />
  </target>

  <target name="rollback" depends="datanucleusenhance"
      description="Rolls back an interrupted application update.">
    <appcfg action="rollback" war="war" />
  </target>

  <target name="request_logs"
      description="Downloads log data from Google App Engine for the application.">
    <appcfg action="request_logs" war="war">
      <options>
        <arg value="--num_days=5"/>
      </options>
      <args>
        <arg value="logs.txt"/>
      </args>
    </appcfg>
  </target>

</project>

图 2:WAR 布局

(project-root)
  /src
    ... (Java source goes here)
  /war
    /index.html
    ... (other directly-browsable elements here)
    /WEB-INF
      web.xml
      appengine-web.xml
      ... (non-browsable elements here)
      /lib
        ... (JAR files)
      /classes
        ... (.class files)

图 3:web.xml

<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
</web-app>

图 4:appengine-web.xml

<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
  <application>Hello</application>
  <version>1</version>
  <threadsafe>true</threadsafe>

</appengine-web-app>
© . All rights reserved.