Java 开发 2.0: 通过 Heroku 的 PaaS 用 Git 提交 Java 应用程序

Heroku 扩展其 Ruby 功能以实现 Java 应用程序可扩展性

Heroku 是一种 PaaS 云服务,为 Java™ 开发格局带来了一些新的内容,从某种程度上讲,是因为其原生语言(和理念体系)是 Ruby。Heroku 将 Ruby 的灵活、乐趣第一的应用程序开发方法与 Git 的智能分布式部署模型相结合,使 Java 开发人员可以通过熟悉的 Java 库访问它们。在本 Java 开发 2.0 系列中,Andrew Glover 使用 Apache Wink、Jetty 和 Maven 构建了一个全新的移动、基于位置的典型应用程序;然后他使用 Git 的高效、可扩展的基础架构将该应用程序部署到 Heroku 中。

Andrew Glover, CTO, App47

/developerworks/i/p-anglover.jpgAndrew Glover 是一名开发人员、作家、演讲家和企业家,他对行为驱动开发、持续集成和敏捷软件开发有巨大的热情。他是 easyb 行为驱动开发 (BDD) 框架的创始人,与他人合著了三本书:Continuous IntegrationGroovy in ActionJava Testing Patterns。您可以通过他的 博客 联系他并通过 Twitter 关注他。



2011 年 12 月 19 日

云中的 IBM

要了解关于云中的 IBM 的更多信息,只需查看一下 IBM SmartCloud Application Services,它是作为服务交付的一种云应用程序平台。您会发现下列详细信息:

  • Application Services:开箱即用的企业级服务,用于协作、分析和业务流程管理
  • Application Life Cycle:支持协作和应用程序开发自动化的技术,同时提供整个应用程序生命周期的可见性
  • Integration Services:跨云环境的简单且高度安全的数据、应用程序和服务集成,由统一的服务管理层提供
  • Workload Services:性能优化服务,从应用程序到工作负载模式,帮助确保服务质量和可用性,同时支持多租户和云之间的工作负载移动性

不要错过 developerWorks 云知识路径,您可以从中学习更多的技巧。

Java 开发 2.0 系列的近期文章已将重点转移到适用于 Java 开发的 PaaS (Platform as a Service) 平台上。此次我将邀请您了解 Heroku,这是最近推出的另一种用来支持 Java 应用程序的流行 PaaS 系统。

Heroku 植根于 Ruby,在它上进行 Java 应用程序开发和部署的方法与之前使用的其他 Java PaaS 选项截然不同,尤其是 Amazon 的 Elastic Beanstalk 和 Google App Engine (GAE)。在开始介绍 Heroku 之前,我认为先了解一下它与这两种平台的共同点和不同之处对您会有所帮助。

GAE 和 Beanstalk:两种重量级类型

我们从 Java 开发 2.0:攀登 Elastic Beanstalk 了解到,Google App Engine 和 Amazon 的 Elastic Beanstalk 在灵活性方面是截然不同的。Java 开发 2.0:使用 Google App Engine 是一个非常紧凑的沙箱,要求您遵守规则;而 Elastic Beanstalk 是完全自定义的:如果在 JVM 上运行它,Amazon 的 PaaS 会支持您使用它。GAE 限制了您的 Java 库选项,而且还控制大多数应用程序的扩展方式。实际上,将应用程序部署到 GAE 是一个放松式的练习:您不用知道 Web 应用程序的位置或者是有多少应用程序实例正在运行。从好的方面讲,Google 承担所有扩展工作,毫无疑问,GAE 应用程序扩展非常好。释放控制权意味着您无需担心太多,当然,除了编写可靠的代码。

关于本系列

自 Java 技术首次诞生以来,Java 开发格局已发生了翻天覆地的变化。得益于成熟的开源框架和可靠的租赁部署基础设施,现在可以迅速而经济地组装、测试、运行和维护 Java 应用程序。在本 系列 中,Andrew Glover 探索使这种新的 Java 开发风格成为可能的各种技术和工具。

Java 开发 2.0:攀登 Elastic Beanstalk 是另一个极端。除了为您提供工具选择外,它还提供了很多控制,控制应用程序如何扩展。这样做的缺点是显而易见的:没有动手参与,Amazon 所能做的工作很少。但是,如果您想要优化应用程序部署,并且想要一个高度可扩展的基础架构,那么 Amazon 的 Elastic Beanstalk 无疑是一个好的选择。

考虑到编码和部署的易用扩展,可将扩展范围设置为 10 至 0(10 表示很痛苦 而 0 表示很轻松)之间的数字,我将 GAE 设置为 4,将 Elastic Beanstalk 设置为 6。GAE 支持我无缝地运行脚本和上传文件,但是有的时候,有限的兼容开发工具并不是我想要的。相反,Elastic Beanstalk 支持我使用想要的任意库,但是部署过程可能更长且有时更枯燥;我会增加复杂性以获得更多控制权。

挑战者 Heroku

与 GAE 和 Elastic Beanstalk 一样,Heroku 旨在进行水平扩展。将您的代码部署到 Heroku 称为 dynos 的地方,从本质上讲,它们是 Web 容器。如果您想要扩展系统,只需添加更多 dynos,便可让 Heroku 处理更多的同步 Web 请求。这是一个简单概念,比 GAE 提供的控制权更多,并且无需满足 Elastic Beanstalk 的配置要求。

Ruby 和 Heroku

尽管 Heroku 开始时的目标是 Ruby 社区(其总公司甚至雇佣了 Ruby 的创始人),Heroku 最近已扩展为支持 Clojure、Node.js 和 Java 语言。尽管 Heroku 的 Ruby 优先方法(和 Ruby 命令行)可能会让一些 Java 开发人员心有余悸,但是我认为它会让差异更明显。具体地讲,Heroku 的基于 Git 的部署模型使构建和扩展基于云的 Web 应用程序变得轻松而又有趣。

对于 Heroku,Git(不是 Ant 或 Maven)是您的部署管道。当您准备好部署应用程序时,可通过 Git push 命令来完成此操作,本文章后面 将详细介绍这一命令。

在易用扩展方面,与 GAE 和 Elastic Beanstalk 相比,Heroku 的扩展范围为 2,这意味着接近很轻松。Heroku 在工具方面提供了极大的灵活性,因此我可以为任意指定的工作选择最高效的工具。在配置方面,Heroku 提供了比 GAE 更多的控制权,但它提供的控制权少于 Elastic Beanstalk,这样的配置有时恰到好处。您还可以轻松地将在 Heroku 上构建的应用程序迁移到 Elastic Beanstalk:如果需要更细粒度的控制,而不是只想了解应用程序的可扩展性,那么可以继续阅读后面的内容!


Heroku 入门

要开始了解 Heroku,您需要安装和设置下列内容:

注意,Maven 不是 Heroku 所必需的,在这里我只将它用作构建工具。Heroku 的 Java 文档也基于 Maven。

构建和运行应用程序

安装所有内容之后,请选择将要工作的目录并运行下列 Maven 命令(为了适应页面宽度,对代码进行了分行):

mvn archetype:generate -DarchetypeGroupId=org.mortbay.jetty.archetype 
  -DarchetypeArtifactId=jetty-archetype-assembler -DarchetypeVersion=7.5.0.RC0

创建 REST 端点

Wink 碰巧是用于创建 RESTful 应用程序的少数可用库之一。在之前的文章中,我使用 Play 和 Gretty 创建了此类应用程序,与 Wink 相比,它们更能简化创建 RESTful 端点的过程。我选择 Wink 是因为 Play 是一个全堆栈框架,在本例中,这正是我需要的。Gretty 与轻量级的 get 一样是轻量级的,这不是我想要的。Wink 不会捆绑 Servlet 容器或包含 ORM 框架,它只会构建 RESTful 资源。在本例中,我认为专业化是一个优点。

Maven 会提示您提供 groupIdartifactId。我通常为 groupId 使用我的包名,为 artifactId 使用我的项目名。然后,Maven 将生成一个项目结构,能够在 Jetty 的基础上构建并运行 Web 应用程序。因此,只需一个步骤,即可搭建可部署到 Heroku 的云基础架构中的 Web 应用程序框架。尽管 Maven 原型使用的是 Jetty,但 Heroku 并不仅在 Web 服务器端支持 Jetty。实际上,Heroku 甚至不知道 Jetty,或者说它没有注意到 Jetty。

之前的 Maven 命令生成的默认应用程序是一个普通的、单调的 "hello world" 应用程序。实际上,如果您浏览到应用程序的 src 目录,然后选择 main—>webapp,就会看到一个 index.html 文件。运行应用程序和该文件将(您可能已经猜到)输出文本 "hello world"。

运行应用程序也非常简单。如果您在新生成的项目目录(具有您为 artifactId 命名的名称)中输入命令 mvn install,将为您所需的操作系统(我的是 OSX)生成一个 shell 脚本。只需输入 $>sh target/bin/webapp,然后在您喜欢的浏览器中输入 http://localhost:8080 即可。


现实世界中的 Heroku

使用 Heroku,您可以部署想要的任何 Java 库。为了举例进行说明 Heroku 并让人们熟悉它(至少为了正在阅读本文的人们),我将构建我的位置收集移动 Web 服务 Magnus 的另一个典型应用程序,已在 Amazon 的 Elastic Beanstalk 简介中介绍了它(参见 参考资料)。对于我的 RESTful 库,我计划使用 Apache Wink,这是 JAX-RS 规范的实现。我的 Web 服务实现将提供一个 PUT 端点,接收 JSON 并将从文档获得的相关数据插入到 MongoDB 实例中。因此,这将通过 Morphia 在 MongoHQ 上进行托管(参见 参考资料)。

我必须做的第一件事是使用新 Wink 和 Morphia 依赖项更新 Magnus 的 Maven pom.xml 文件,如清单 1 所示:

清单 1. 将 Wink 和 Morphia 添加到 Maven POM
<dependency>
 <groupId>org.apache.wink</groupId>
 <artifactId>wink-server</artifactId>
 <version>1.1.3-incubating</version>
</dependency>

<dependency>
 <groupId>org.apache.wink</groupId>
 <artifactId>wink-json-provider</artifactId>
 <version>1.1.3-incubating</version>
</dependency>

<dependency>
 <groupId>com.google.code.morphia</groupId>
 <artifactId>morphia</artifactId>
 <version>0.99</version>
</dependency>

请注意,我还更新了 POM 文件,以查看内部的 Morphia Maven 存储库并获得版本 0.99:

清单 2. 将新的存储库添加到 Maven POM
<repositories>
 <repository>
  <id>morphia repository</id>
  <url>http://morphia.googlecode.com/svn/mavenrepo/</url>
 </repository>
</repositories>

接下来,我将创建位置资源,即一个将表示用户位置的 Wink 端点:

清单 3. 创建一个 Wink LocationResource
@Path("/location")
public class LocationResource {

 @PUT
 @Consumes(MediaType.APPLICATION_JSON)
 @Path("{id}")
 public String updateAccountLocation(@PathParam("id") int accountId, JSONObject 
  requestJSON) {
  try{

   double latitude = Double.parseDouble(requestJSON.get("latitude").toString());
   double longitude = Double.parseDouble(requestJSON.get("longitude").toString());

   SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm");
   Date dt = formatter.parse(requestJSON.get("timestamp").toString());

   new Location(accountId, dt, latitude, longitude).save();
   return new JSONObject().put("status", "success").toString();
  }catch(Exception e){
   return "failure with " + requestJSON;
  }
 } 
}

Wink 让我可以通过三个注释来描述我的 RESTful 服务:一个注释描述我的 HTTP 方法 (PUT),一个描述我的预期请求内容类型 (JSON),最后一个表明端点接受参数(在本例中是 location/:accountId)。

Location 类是我在 Elastic Beanstalk 简介 中介绍的 Morphia-backed 对象。它仅在 MongoHQ 中创建一个文档,表示指定帐户的位置。位置(从理论上讲,是通过移动设备接收的)由 RESTful 端点的参数表示。

Wink 和 Jetty

接下来,我想将 Wink 与 Jetty 相连接,因此我需要进行两个操作:创建一个 Application 类并在我的 web.xml 文件中进行配置。

Wink Application 类(如清单 4 所示)的用途是加载相应的资源类:

清单 4. 一个 Wink Application 类
public class MarmarisApplication extends Application {
 @Override
 public Set<Class<?>> getClasses() {
  Set<Class<?>> classes = new HashSet<Class<?>>();
  classes.add(LocationResource.class);
  return classes;
 }
}

在清单 5 中,我将更新应用程序的 web.xml 文件,添加特定于 Wink 的属性,比如指向我的 Application 类的一个指针和所需的 URL 模式,在本例中是 /service/resource

清单 5. 通过 web.xml 连接 Wink
<servlet>
 <servlet-name>MarmarisApp</servlet-name>
 <servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class>
 <init-param>
  <param-name>javax.ws.rs.Application</param-name>
  <param-value>com.b50.marmaris.MarmarisApplication</param-value>
 </init-param>
 <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
 <servlet-name>MarmarisApp</servlet-name>
 <url-pattern>/service/*</url-pattern>
</servlet-mapping>

作为测试,可以尝试重新运行 mvn install 命令,并在本地启用应用程序(在本例中是 Magnus)。您应该能够将 JSON 文档(如清单 6 所示) 提交到端点 http://localhost:8080/service/location/{account_id},其中 account_id 是一个数字。

清单 6. 表示位置的一个 JSON 文档
{
 "name":"location payload",
 "latitude":"46.49",
 "longitude":"71.11",
 "timestamp":"09-02-2011 14:43"
}

编写此应用程序并不难,现在我们将开始一个更简单的部分:在 Heroku 的云中部署它!


Heroku 和 Git

Heroku 的开发管道是 Git,如果您不熟悉分布式版本控制,可进行一些调整。(通过 Git)部署到 Heroku 类似于将 Subversion 提交发送到主管道外的一个分支。但是,在本例中,Heroku 不是主代码存储库;它只是一个备用远程存储库。要部署应用程序,将通过 Git 对其源代码执行 push 操作。

注意,不使用 war 文件,Heroku 将一原生的形式来部署您的项目。正如当我们开始构建应用程序的 Procfile 时所看到的那样,Heroku 只是寻找要执行的代码的一个 JVM。(如果您现在想了解我谈论的内容,请参阅在项目的包结构中生成的 Main.java 文件,然后将其中的内容与您 POM 中的各项相关联。)

Git 提示

如果您是 Github 用户,那么可以将 Heroku 看作另一个存储库,您会在其中推送想要的代码基分支,比如开发、QA 或阶段。

尽管此处的描述不是非常直观,但当您开始使用 Heroku 的部署模型时就会发现它很有用。此外,Heroku 与 Git 的紧密集成使得将各种分支推送到不同环境中变得快速而又简捷。

两阶段部署

部署设置分两个步骤进行:首先,您需要创建代码并将它提交到一个本地 Git 存储库。可以在项目的根目录中完成此操作,方法是在终端中输入清单 7 中的命令:

清单 7. Git 初始化和提交
$> git init
$> git add .
$> git commit -m "initial commit before Heroko deployment"

现在,您的本地 Git 存储库包含了代码的快照(即已对代码进行了版本控制)。

接下来,要在 Heroku 中创建一个应用程序。我已在清单 8 中通过 heroku 命令行客户端(这里是有必要安装 Ruby 和 RubyGems 的位置)完成了这一操作:

清单 8. 创建 Heroku 应用程序
$> heroku create marmaris --stack cedar

清单 8 中的 heroku create 命令在 cedar stack 上创建了一个名为 "marmaris" 的应用程序。注意,您必须选择一个不同的应用程序名称,比如此时采用的名称。此外,可以将名称保留为 Heroku,它将为您生成一个独一无二的名称。

Heroku 有许多堆栈。Cedar 支持 Java 和 Node.js,而其他平台(比如 Bamboo)支持较新的 Ruby 版本。当您执行 heroku create 命令时,将更新您的 Git 配置并添加一个名为 heroku 的远程存储库。

Heroku 的 Procfile

在将代码基部署到 Heroku 之前,您需要告诉它如何运行应用程序。可使用 Procfile 轻松地完成这项操作,它只是一个描述命令的文本文件。如下所示,我的 Procfile 指向 target 目录中的 webapp shell 脚本。

清单 9. 创建 Procfile
$> echo 'web: sh target/bin/webapp' > Procfile

创建了新文件后,通知 Git 也很重要,否则在您通过 Git 对部署应用程序执行 push 操作时,Heroku 有可能不知道该程序。

清单 10. 通知 Git
$> git add Profile
$> git commit -m "updated to include my Profile"

最后,要部署应用程序,只需将 Git pushheroku 远程存储库重即可,如清单 11 所示:

清单 11. 部署到 Heroku
$> git push heroku

您应该看到 heroku 返回的一系列信息,请寻找类似下列内容的一则信息:

http://your_app_name.herokuapp.com deployed to Heroku

在您喜欢的浏览器中输入 URL,(假设您在项目目录中保留了默认的 index.html 文件)您将看到 "hello, world!" 输出。

尽管 "hello, world!" 总是十分有趣,但此应用程序的用途是通过 HTTP PUT 接收位置信息。因此,通过使用 WizTools.org 的 RESTClient,我可以根据 RESTful 端点发出 HTTP PUT 命令,提供一个 JSON 文档。我使用词 success 发送了一个很好的 JSON 响应。


扩展和维护 Heroku

默认情况下,Heroku 在一个 dyno 上运行您的应用程序。此 dyno 是免费的,实际上,它会在没有活动时自行关闭,并在出现请求时开启。如果您需要扩展应用程序,可添加更多 dyno,这可通过 heroku scale 命令来完成:

清单 12. 扩展应用程序
$> heroku scale web=2

如果您需要缩小应用程序的规模,还可以通过减少请求 dyno 的数量来缩减应用程序。例如,在本例中,我将该数量缩减为 web=1。也可通过 Heroku 的 Web 界面来完成这两项操作。

您还可以通过清单 13 中的 heroku logs 命令在 Heroku 上跟踪日志:

清单 13. 实时查看 Heroku 上的日志
$> heroku logs -t

heroku 命令行客户端支持许多特性来扩展、监控并管理您的应用程序;参见 参考资料 了解文档。Heroku 还支持许多第三方加载项,除了默认的数据存储。我建议检查 Heroku 对 MongoHQ 和 PostgreSQL 的支持。


结束语

播客:Adam Wiggins and Jesper Joergensen on Heroku

此播客 中,了解关于 Heroku 入门知识、多语言支持和如何开始的更多信息。

Heroku 与 Git 的紧密集成提供了一种通过云部署和扩展 Java 应用程序的新模式,但是这也使过程变得非常简单且强大。总之,自由使用无限的 Java 库,然后轻松地部署生成的应用程序,这的确很适合我。

参考资料

学习

获得产品和技术

讨论

  • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, Cloud computing
ArticleID=781606
ArticleTitle=Java 开发 2.0: 通过 Heroku 的 PaaS 用 Git 提交 Java 应用程序
publish-date=12192011