PaaS 是一种云服务类型,其中供应商不但提供按需硬件和操作系统服务,而且还提供应用程序平台和解决方案堆栈。PaaS 服务可将与应用程序部署关联的大多数 IT 管理方面自动化,包括资源配置、分段和测试、负载平衡、数据库访问以及访问平台库。PaaS 的关键功能是多组织体系结构:即多个不相关的应用程序可运行在相同的硬件和软件基础设施上,从而节约成本以及更有效地利用计算资源。开发人员只需关注应用程序本身,而不需要关注部署和 IT 问题。
Java 开发人员能够很好地了解并利用 PaaS 的开发模型。毕竟,在早期服务器端 Java 中,PaaS 的概念就已经深深地植入了。当时,IT 组织出售使用应用程序服务器作为 “容器” 的远景,然后再将其放入应用程序存档文件中以便在共享资源环境中运行(请参考
PaaS 的前身 侧边栏)。这一远景与我们今天所看到的 PaaS 服务非常相似。
但是 Java 企业应用程序的早期 PaaS 远景没有成功。Java 应用程序服务器从来没有稳定到可以随意地部署和取消部署多个不相关的应用程序。存档结构最后将开销添加到 Java 应用程序开发周期中:鉴于 Rails 上的 PHP 和 Ruby,开发人员可以更改某行代码并重新加载浏览器以便查看不同,然而 Java 开发人员必须重新编译,重新包装并重新部署其应用程序,并经常重新启动应用程序服务器。
事实证明,PaaS 远景仅随着远比 JVM 先进的新一代虚拟化技术而实现。随着 Google App Engine 的开创,新一代 Java PaaS 服务履行了 Java EE 的旧承诺。同时它们提供随您需求成长的按需支付的 IT 基础设施而无需您在昂贵的硬件和系统管理功能上投入太多。
在本文中,我将会研究三个主要的 Java 公开 PaaS 产品以便比较它们的方法、优点和缺点。这三种都提供同一套功能,包括:
- 上传并部署应用程序 WAR
- 版本控制已部署的应用程序
- 测试并分段环境
- 在线访问日志文件
- 自动监控和使用报告
然而除了那些共同的功能以外就是一些重要的区别。鉴于我自己使用这些新兴技术的经验,我将为比较它们提供框架并讨论潜在的解决方法以便在您使用它们时帮助您避免问题。
Google App Engine (GAE) 是第一个被广泛采用 Java PaaS 平台。(Java 版本有时被称为 GAE/J,以便将其与基于 GAE Python 的 PaaS 产品中区分开来。)它也可能是市场上 “最纯净” 的 PaaS 产品 — 在这个意义上它几乎完全为开发人员抽象化了底层基础架构。
从 2009 年开始,GAE 就已经支持 Java 平台作为开发和部署环境。然而,GAE 的 Java 支持是有限的且不符合标准。由于它在其应用程序上强加诸多限制 — 它们中的许多都有充分的理由来维持可伸缩性 — GAE 不支持某些 Java 平台 API:最明显的是,文件写入 I/O(因为 GAE 不对应用程序提供文件系统访问)和许多网络 I/O API(因为 GAE 在源于应用程序的网络操作上施加了严格的限制)。要获得 GAE 所支持的 Java 平台 API 的完整列表,请参考
参考资料。
通过支持其自己的有限网络 I/O API,GAE 限制了应用程序连接到其他服务的能力。GAE 名义上允许应用程序出站连接其他服务器。但为了在可控的系统中保持线程数,GAE 会强迫任何应用程序发起的连接在 5 到 10 秒后关闭。这使 GAE 成为不可靠混合类型应用程序平台。对于越来越多的使用第三方 web 服务 API 的应用程序来说,这就是 GAE 的主要限制。
此外,在您需要使用现有应用程序框架或将现有应用程序移动到 GAE 时,这些 API 限制构成了挑战。经过多年的演化,企业 Java 开发在很大程度上依赖于框架。虽然在 GAE 上一些流行的框架(如 Spring 和 Struts)都是开箱即用的,但是其他一些要么不工作要么需要对其源代码打补丁。因为您基本上是正在创建一个打破上游兼容性的分支,所以手动获取框架源代码以便使其在 GAE 上运行永远都不是一个好主意,且其可能将难于调试的错误引入框架。一个好的示例是 JavaServer Faces (JSF) web 框架:其需要源代码级获取以便在 GAE 环境中运行,即使如此在 JSF 顶端的许多 UI 库都兼容 GAE。(要获得一系列支持 GAE 的 Java 框架,请参考
参考资料。)
同样地,已经开发的大型企业应用程序可能使用 GAE 禁止的 API。将这些应用程序迁移到 GAE 可能是昂贵的,因为您不仅需要识别问题并创建解决方法,而且还要从头再为整个应用程序做质量保证。
如果不支持 Java 平台 API 的一部分,那么 GAE 将不能履行 Java 关于 “编写一次,随处可用” 的承诺。虽然这对于许多人来说并不是致命的弱点,但这是潜在用户需要意识到的。
GAE 承诺并传递可伸缩性,但不一定是原始性能。Web 应用程序的原始性能是通过对 web 请求的响应时间来衡量的。可伸缩性是指无论多少用户正在访问系统,平台都能保持一致响应时间的能力。例如,3 秒钟对 100 个并发用户作出响应的可伸缩的系统应该也可以 3 秒钟对 100 万并发用户作出响应。
GAE 提供出色的可伸缩性就像通过一致响应时间所衡量的那样。但是其原始性能通常是缓慢的。以我的经验,GAE 常常用 1 到 3 秒对数据库相关请求作出响应。
该特点对应用程序开发人员有明显影响。对于在大部分时间里空闲的 web 应用程序来说(即大多数小型 web 应用程序),在 GAE 基础设施上进行部署不会产生性能优势,即使是在低端虚拟专用服务器上。在您需要扩展应用程序远远超越低端服务器硬件容量时,真正的性能优势才会到来。
低流量网站的另一个问题是 GAE 将无效(inactive) JVM 换出(swap)内存,以便在系统中优化高流量 web 应用程序。如果您的 JVM 被换出内存,那么在下一次请求到来时,GAE 必须花费更多的时间来启动整个应用程序。对于低流量 web 应用程序来说,这可能导致缓慢的性能(第一次请求的等待时间超过 5 秒钟)。为了获得更一致的性能,GAE 为开发人员提供付费的选择让无效的 JVM 保存在内存中。一个建议:在 GAE 内建立 cron 作业以便每 2 到 3 分钟加载一次您自己的网站,从而保持 JVM 活跃。
GAE 的关键创新就是使用了真正可伸缩的数据存储:即 Google BigTable。大多数 web 应用程序都使用关系数据库作为后端数据。但是关系数据库难于扩展是出了名的。要解决此问题,Google 的研究人员开发了一个名为 BigTable 的替代数据存储解决方案,它是 NoSQL 数据库世界中的数据存储解决方案之一。
正如在关系数据库中那样,BigTable 中的数据可以组成具有行和列的表,且每一行都有一个惟一的索引 ID。不像关系数据库那样,BigTable 表没有固定的模式且通常是非规范化(denormalized)的。表中的每一行可能都有不同的列。相对于通过键列跨不同的表链接不同行,最佳实践将是在一行中有许多列。对于数模型的设计来说这已经产生了重大的影响。为了便于检索应用程序,开发人员被鼓励将冗余信息放入每一行,而不是设计规范化的关系模型。想一想 web 服务器的访问日志,其中每一行都重复了 IP 地址和浏览器代理,这虽然占用了空间但却简化了批量处理。
BigTable 的优点是可伸缩性。Google 工程师宣称 BigTable 中数据查询的响应时间只根据结果数据集的大小确定。无论查询是针对 1000 行的表或者 1 亿行的表,您都可以获得同样的性能,只要结果被限制为 1000 行。就其本身而言,GAE 将每次查询的返回数据集限定为 1000 行。
调整到 NoSQL 范例,虽然它可能对来自 SQL 背景的开发人员来说具有挑战性,但是对于正在面临 “大数据” 挑战的越来越多的 IT 组织来说,这是一个重要的技能。我发现 GAE 是 Java 开发人员开始了解 NoSQL 的最佳和最容易的地方之一。
然而,虽然 BigTable 对于 GAE 的强大可伸缩性来说是关键,但是其目前的执行留下了很多有待 Java 开发人员改进的地方。BigTable 的具体缺陷(以及一些潜在的解决方法)包括:
- 微弱的数据查询支持:以 Google 查询语言(Google Query Language,GQL)编写的查询用于从 BigTable 检索数据。GAE 需要将查询中涉及到的所有数据列编入索引,且该索引不包含 BLOB 或文本列。这很好,除了 GAE 只允许每个表 100 个索引以外。虽然这对于标准 SQL 数据库来说可能足够了,但是像 BigTable 那样的非规范化 NoSQL 数据库可能潜在具有数以千计的列,因此 100 个索引可能会限制很多应用程序。更糟的是,GAE 没有提供简单的方式来删除不再使用的索引。
决定要创建哪个索引对于 GAE 开发人员来说是一个很大的负担。如果查询使用没有进行索引的列的组合,那么当执行查询时,GAE 将只在运行时出现一个异常。虽然由于您在本地计算机上测试应用程序而导致 SDK 可为自动生成索引配置文件提供工具,但是如果您没有手动地详尽测试所有执行路径,那么您可能会一直错过索引。将自动生成的索引合并到已经部署的应用程序中也是一个潜在的容易出错的过程,该过程直到 web 应用程序用户点击错误配置的索引前都没有错误提示。
最后,这有点让人震惊 — 考虑到 BigTable 是 Google 产品 — 在数据库中不支持免费的文本搜索。您可以将搜索引擎实现(如 Apache Lucene)嵌入您的应用程序,以便索引并搜索文本列(请参考
参考资料)。但是对于那些标准 SQL
LIKE
语句就足以进行简单文本搜索的小型网站来说,这就是一个大麻烦。
- 导入和导出数据的难题:BigTable 的另一个主要问题是无法导入和导出数据。因为没有直接访问 BigTable 的标准 API,所以在您自己的应用程序内,您必须将数据导入和数据导出逻辑写入 servlet,并使用您自己的 web 界面来导入或导出数据。
因为 GAE 会在 30 秒以后终止任何 web 请求线程,所以不可能通过持久连接将大量数据上传到 BigTable。常用的解决方法就是将数据导入分成许多块,且每一块都要求上传并处理的时间少于 30 秒。然后,您可以使用自动 HTTP 设备,如 JMeter 或 Grinder,以便一个接一个地运行这些任务直到所有数据都被导入。不用说,这将是一个繁琐的过程。
从 BigTable 导出数据更成问题。因为 API 将每个数据查询限制为 1000 条结果,所以导出数据必须在比 30 秒处理超时限制所允许的还要小的块中进行管理。
认识到 BigTable 对于大多数开发人员的局限性,GAE 就可以通过其付费业务产品对已托管的 MySQL 服务提供访问。
GAE 提供与其他 Google 服务的出色集成。值得注意的是,应用程序可与 Google Accounts 集成在一起,以便用户使用 Google 用户名和密码登录应用程序。鉴于构建用户管理系统是每个网站都必须做的重复工作,所以这可能潜在地为您节约时间。然而,缺点是并非所有的用户都有 Google 帐户,且将您的网站与 Google 帐户捆绑使得更难于移动到另一个 PaaS 供应商。
GAE 应用程序也可使用简单 API 以便通过 GMail 服务器发送电子邮件。相对于不安全的 SMTP 服务器,不太可能通过收件人 ISP 阻塞 GMail 服务器。
如果您在 Google Apps 上托管您的域,那么通过将 Google Apps 帐户与 GAE 帐户链接,您还可以配置通过任何在您控制下的子域访问的应用程序。例如,如果通过 Google Apps 托管 mydomain.com,那么您就可以从 www.mydomain.com 而不是 mydomain.appspot.com 访问应用程序。
总体而言,GAE 提供了精心设计并可伸缩的 PaaS。对于小型网站来说,其慷慨的免费配额也是很吸引人的。然而,缺乏对完整 Java 平台的支持是一个潜在的致命伤,且 GAE 中的一些组件尚处于试验阶段而不是已经生产就绪。
Amazon Elastic Beanstalk(来自 Amazon Web Services 的相对新的产品)提供了基于 Amazon Elastic Computing Cloud (EC2) 基础设施的受管理的 Apache Tomcat 运行时环境。EC2 是 Infrastructure-as-a-Service (IaaS) 产品,因此它可以提供比 GAE 更多的灵活性。但是作为权衡,它也需要更多的开发人员努力对应用程序进行管理和扩展。
Beanstalk 环境支持运行在 EC2 虚拟服务器上的完全 Tomcat 服务器。它是一个可访问基础文件系统的纯 Java 环境。因为 Tomcat 的声望,所以几乎所有企业 Java 框架都支持 Tomcat 部署。这些框架可从 Tomcat WAR 文件启动或引导,并为您提供广泛的框架和库选择。
普通 Tomcat 运行时对线程以及文件或网络 I/O 没有限制。只要需要网络 I/O 线程就可以一直保持打开。您只受限于基础虚拟机的容量。
通过自动启动新的 EC2 实例并将您的 WAR 文件部署到新的实例,Beanstalk 可以扩展您的应用程序。所有 Beanstalk EC2 实例都正运行在负载平衡器后面。您可以使用基于 web 的管理控制台来监控可用于每一个 EC2 实例上的资源,并设置规则,从而在现有服务器负载超过预设限制时自动启动负载平衡器后面的新服务器实例。
负载平衡 web 集群中常见的问题是如何处理 HTTP 会话。每一个 Tomcat 服务器节点都可以为其客户端创建并管理会话对象。如果跨多个服务器节点负载平衡 web 请求,那么您需要确保服务于请求的服务器节点都有正确的会话对象。实现其的简单办法是在负载平衡器中启用 “粘性会话(sticky session)”,这需要负载平衡器记住通过其后面的每一个服务器保持的会话 cookies,并将请求转发到基于传入 cookies 的正确服务器。可在 Beanstalk 负载平衡器管理控制台中打开 “粘性会话”。更有效的和防止故障的解决方案包括跨服务器节点建立共享的内存或将会话对象简单保存到中央数据库。因为每一个服务器节点都有相同的对话状态信息,所以这些选项允许负载平衡器将请求转发到随机或最繁忙的服务器节点。但是所有这些选项都需要来自应用程序开发人员的努力。不同于 GAE,其自动将会话数据保存到 BigTable,Beanstalk 需要您做所有的工作。
也许 Beanstalk 最大的缺陷之一就是其价格,尤其是对于可以在其他地方获得免费托管的小型网络。虽然 Amazon EC2 有一个 “一年免费” 计划提供给新注册的用户,但是 Beanstalk 的标准价格即使对于单节点安装来说也要接近每个月 40 美元。这对于需要时在短短几分钟内就可以自动向外扩展的集群就绪的基础设施来说是便宜的价格,但是如果您的应用程序除了偶然的流量激增以外大都处于闲置,那么相对于 GAE 来说就比较贵了。
Elastic Beanstalk 平台的优点之一就是在选择数据库技术上的灵活性。其提供以下几个选项:
- 关系数据库:通过 Amazon 自己的关系数据库服务(Relational Database Service,RDS),您可以部署各种关系数据库。这些数据库服务器都通过 Amazon 管理并监控,这很容易将数据导入并从中将其导出。在您的应用程序内,所有您需要做的就是将数据源指向 RDS 服务器。但是请注意每一个 RDS 实例都是另一个运行数据库的专用服务器实例 — 数据库实例比具有可比性的 EC2 实例贵 30%。成本可以积累,且许多应用程序不需要专用数据库服务器。
- NoSQL:与 RDS 服务器同样的问题就是它是一个难于扩展的关系数据库。如果您喜欢类似于 Google BigTable 的 NoSQL 方法,那么它也可与 Amazon SimpleDB 一起使用。SimpleDB 的 Java API 可让您的应用程序轻松访问数据。
- 您自己的数据库服务器:因为 EC2 提供对原始虚拟服务器的访问,所以您可以在独立的 EC2 实例上建立自己的数据库或 NoSQL 数据源(如 Apache Cassandra)并只将 Beanstalk 应用程序指向您自己的数据库服务器。
数据库选择中的灵活性(尤其是使用 Amazon 托管关系数据库的能力)很可能吸引企业开发人员。
除了 Amazon RDS 和 SimpleDB,Beanstalk 服务器可访问其他 Amazon 服务,如 Simple Queue Service、S3 Storage、Simple Email Service (SES) 以及付费 API。SES 特别有趣并提供了与 GAE 中的 GMail API 的很好比较点。
SES 有一个简单的 API,其允许您使用 Amazon 的 SMTP 服务器发送电子邮件。相对于在您自己的 EC2 实例上建立不安全的 SMTP 服务器来说,使用 Amazon SMTP 服务器的优点就是,Amazon 服务器不太可能被主要 ISP 的垃圾邮件过滤器封锁。为此,SES 提供了一系列丰富的工具以便控制高速增长的电子邮件数量并接收来自 ISP 垃圾邮件过滤器的反馈。所有这些功能都被提供给您的 Beanstalk 应用程序,以便您可以监控您的活动,并为了更有效的交付而优化您的电子邮件内容。
总体而言,Amazon Elastic Beanstalk 大大简化了 Tomcat 应用程序的部署和扩展。然而,它一直提供基本 EC2 基础设施的灵活性,这使其非常适合企业应用程序。然而,对于低流量网站或业余开发人员来说其成本是很高的。
CloudBees 是 Java PaaS 场景的新参与者。虽然它可能只是一个开始,但是其后面的开发人员都是企业 Java 的老手。(它由 JBoss 前 CTO Sacha Labourey 启动,并聘用了开源 Java 重量级人物以 JBoss 闻名的 Adrian Brock 和以 Hudson 闻名的 Kohsuke Kawaguchi。)其 PaaS 技术是从 Stax Networks 收购的,该公司已经对企业客户提供托管 Java 应用程序服务超过 10 年。CloudBees RUN@Cloud 服务基于健全的 Stax 平台,并通过自服务 web 门户提供给单个开发人员。
与大公司相比,RUN@Cloud 旨在受管理的可伸缩性(如在 GAE 中)和灵活性(如在 Amazon 的 PaaS 服务中)之间发现正确的平衡,同时通过该平台添加自己的端对端开发生命周期支持。
RUN@Cloud 服务目前基于 EC2 基础设施,可以将其看做自动化程度更高的 Beanstalk + RDS 版本。与 Beanstalk 一样,RUN@Cloud 也为每一个 web 应用程序提供在 EC2 虚拟服务器上运行的专用 Tomcat 实例。其提供纯 Java 环境,没有对文件系统访问、网络 I/O 以及线程的人为限制。
作为小型独立公司,RUN@Cloud 的优点之一就是无需与 Amazon 捆绑在一起。在不久的将来,其计划提供其他基础设施供应商以便补充 EC2。
也类似于 Beanstalk,RUN@Cloud 提供了可扩展的基础设施,将按需启动负载平衡器和服务器实例以满足流量激增。但是 RUN@Cloud 比 Beanstalk 提供了更多的自动化。例如,RUN@Cloud 已经配置了其 Tomcat 服务器,以便将会话保存到其管理下的数据库中,而不是使用 “粘性会话”。此托管会话对象数据库对开发人员透明 — 这很像 GAE。
因为 RUN@Cloud 可以使用共享的负载平衡器来管理在单个 EC2 实例上运行的多个 Tomcat 服务器,所以其无需每个 Tomcat 实例都有一个 EC2 实例。因此它可以用比 Beanstalk 低的多的成本运行低流量网站。实际上,RUN@Cloud 有一个对于低流量应用程序或业余开发人员以及学生来说非常好的免费使用层。
然而,也像 GAE 那样,如果应用程序长时间处于不活动状态,那么 RUN@Cloud 可以将您的 JVM 交换出内存。这可能会导致对第一个请求的缓慢响应,就像应用程序在 “预热”。
RUN@Cloud 服务本身支持与 Tomcat 服务并列的托管 MySQL 服务。您可以通过基于 web 的管理控制台创建并管理数据库。您可以通过 MySQL 客户端直接连接到数据库服务器以便管理您的数据。
不同于 Amazon RDS,RUN@Cloud 服务跨多个应用程序部署共享数据库服务器。每一个应用程序都有自己的数据库但不一定是专用的服务器。PaaS 平台自动部署数据库以便最大化利用数据库服务器。与 RDS 相比,共享数据库服务器可能更有效地利用虚拟服务器,从而降低成本。
RUN@Cloud 对通过基本基础设施供应商支持的平台 API 和服务提供访问。特别是对于在 Amazon EC2 上部署的 RUN@Cloud 应用程序来说,这些应用程序可以从您的应用程序内完全享有所有的 Amazon web 服务 API — 如 S3、SQS 以及 SES。
但是 RUN@Cloud 真正的亮点是其紧密地与 DEV@Cloud(基于云的 Continuous Integration 平台)集成在一起。DEV@Cloud 提供源代码、版本控制系统(Subversion 和 GIT);一个生成库 (Apache Maven);以及生成服务器(jenkins,以前称为 Hudson)。其允许您在云中而不是在您自己的计算机上运行应用程序的自动化生成和测试。这种类型的集中生成系统被灵敏软件团队广泛采用,以便确保总是测试库中的源代码且该代码处于可释放状态。
通过将 RUN@Cloud 与 DEV@Cloud 集成在一起,CloudBees 提供了一系列引人注目的 PaaS 服务,这些服务可以管理企业 Java web 应用程序的整个开发、测试以及部署周期。您只需在您自己的计算机上编辑源代码,所有一切在云中都可以用最小的 IT 开销委派给自动化系统。
CloudBees RUN@Cloud 是 Amazon Elastic Beanstalk 和 RDS 的低成本(甚至免费的)替代品。它集成了连续构建系统,使其可以引起那些希望在开发过程中自动化所有 IT 功能的敏捷软件开发团队的注意。
在经过了多年的失望之后,Java PaaS 服务最终达到了黄金时代。在本文中回顾并比较的三种服务中的每一种都有其独特的方法,因此具有独特的优点和缺点。
如果您正在开发新的应用程序并可以忍受 GAE 的限制,那么 GAE 就是出色且免费的选择。RUN@Cloud 和 Elastic Beanstalk 都是应用程序级的可内部交换的运行时。标准的 Java EE 应用程序可以在任何平台上运行而不需要进行修改。RUN@Cloud 起步更便宜且更易于配置,其为连续集成的开发过程提供出色的支持。我建议用免费的 RUN@Cloud 起步,因为如果您不愿意使用 CloudBees 的服务,您可以轻松地转换到 Elastic Beanstalk。