Graeme Johnson 和 Michael Dawson
2013 年 11 月 21 日发布/更新: 2014 年 9 月 25 日php
0java
云供应商必须权衡运行系统和提供服务所需的基础架构的成本与供应商所得到的效益。这些成本效益考虑因素促使供应商考虑应采用多种架构。他们的选择范围涵盖从无共享 架构到共享多租户 架构的一系列架构。在无共享架构中,供应商提供了彻底专用于每一个客户的硬件、软件和应用程序。在共享多租户的架构中,可使用单个应用程序支持多个客户的应用程序,并且全部底层的硬件和软件都是共享的。linux
沿着这个架构系列进行发展时的主要权衡因素是隔离 和密度。密度是由一组特定的硬件和软件交付的系统和服务的数量。共享的资源越多,密度就越高。密度越高,供应商的成本也就越低。同时,更多共享能够下降租户 之间的隔离水平,这里的租户指的是所提供的单独系统或服务。隔离是一个租户对其余租户的活动和数据的影响程度。编程
对于基于 Java 的租户,在架构系列中的定位包括共享或不共享 JVM。在共享顶层应用程序的任何架构中,必须共享 JVM。共享 JVM 既能节省内存,又能节省处理器时间。可是,若是使用传统的 JVM 技术,那么共享 JVM 一般会从基础架构层中删除全部剩余的隔离,顶层应用程序被要求自身提供这种隔离。 在 IBM 的 7 R1 版中,以技术预览的形式提供了对本文介绍的多租户功能的试用(请参阅 参考资料)。此特性使得用户能够经过部署得到共享 JVM 的优势,尽管与共享传统 JVM 时得到的隔离水平相比,该特性能够提供更高水平的隔离。安全
使用多租户 JVM 的主要优点是,能够经过部署来避免一般与使用多个标准 JVM 有关的内存消耗。此开销是由如下几个缘由形成的:服务器
String
和 Hashtable
,都存在于全部应用程序中)占用了一些内存。每一个 JVM 都存在全部这些构件的一个实例。除了下降内存和处理成本以外,与在单一传统 JVM 中运行多个应用程序相比,多租户 JVM 提供了更好的隔离。数据结构
另外一个好处是,从共享 JVM 的第一租户开始,后续的应用程序须要更少的时间来启动,由于 JVM 已经在运行。减小启动时间对于短时间运行的应用程序特别有用,这些应用程序一般用于脚本。架构
使用多租户 JVM 的主要成本是,相对于在单独的 JVM 中运行的多个应用程序,租户的隔离水平较低。例如,多租户 JVM 中的本机崩溃会影响全部租户。并发
另外,JVM 为了实现多租户扩展而必须作的一些工做会致使微小的性能降低。不过,这种性能影响会随着租户数量的增长而下降,由于在同一系统中运行多个 JVM 能够避免增长处理器和内存成本。
为了选择与其余租户共享一个运行时,当启动应用程序时,应用程序用户在命令行中添加一个 -Xmt
参数。例如:
1 |
|
其结果是,应用程序的行为(因为咱们在本文稍后描述的 限制)就好像它在一个专用 JVM 上运行同样。但在现实中,它是与其余应用程序并行运行的。多租户 JVM 中的扩展支持这种方式的启动,并提供了共享 JVM 的租户之间的隔离。
当租户启动时,JVM 启动程序要么会定位现有的共享 JVM 守护进程 (javad
) ,要么会启动它(若有必要),如图 1 所示:
图 1. JVM 启动程序自动定位了共享的 JVM 守护进程(若是有必要,还会启动它)
当第二个租户启动时,租户会发现现有的共享 JVM 守护进程,并在该 JVM 内运行,如图 2 所示:
图 2. JVM 启动程序被定位并链接到现有的 JVM 守护进程
结果,javad
进程中存在两个租户共用的引导代码的一个副本。这样的安排使得租户能够共享大部分运行时结构。
使用多租户 JVM 运行现有的应用程序很容易,由于只需更改有限的命令行便可。
在相同的(常规) JVM 中运行的两个或两个以上的应用程序一般不会彼此隔离。每一个应用程序的活动都会影响另外一个应用程序的结果。此外,经过静态字段共享的数据也能够供全部应用程序访问。多租户 JVM 有两种解决这些问题的方法:静态字段隔离 和资源限制。
在多租户 JVM 中,各租户之间共享类的不变的部分。这些部分包括编译后的方法代码、JVM 使用的数据结构,以及其余相似的构件。此分享能够节省内存,由于没必要在使用多个 JVM 的状况下提供独立的副本。然而,多租户 JVM 为每一个租户提供了它们本身的静态字段副本。因为静态字段隔离(以及每一个租户一般只能访问本身建立的实例的对象这个事实),每一个租户只能访问与自身有关联的数据。其结果是租户之间的数据隔离。
在一个完美的世界中,租户可以以适当的方式共同运营和使用共享资源。然而,在这个不完美的世界中,程序错误和恶意行为都有可能出现。多租户 JVM 提供了可配置的控制,以便限制租户执行错误操做和以影响其余租户的方式使用资源的能力。能够控制的值包括:
能够在 -Xmt
命令行中指定这些控制。例如:
-Xlimit:cpu=10-30
(最小 10% 的 CPU,最大 30%)-Xlimit:cpu=30
(最大 30% 的 CPU)-Xlimit:netIO=20M
(最大带宽为 20 Mbps)-Xms8m-Xmx64m
(初始的堆大小为 8 MB,最大值 64 MB)Java 7 R1 文档包括全部可用选项的信息(请参阅 参考资料)。
为了比较无共享 JVM 和多租户 JVM 上的应用程序性能和内存占用,在该测试中,咱们将应用程序添加到每一个 JVM 配置,直到执行系统置换(system swap)。(当系统置换时,咱们认为该系统是 “满的”。)在无共享的状况下,咱们在单独的 JVM 中运行应用程序,并为每一个额外的应用程序启动一个新的 JVM。在多租户的状况下,咱们在单一的多租户 JVM 中将应用程序做为另外一个租户运行。
表 1 和 表 2 显示了咱们使用一台配置了 1 GB 内存的计算机和一个 64 位 JVM(在全部状况下,都使用了压缩的引用 JVM 和均衡的垃圾回收策略)所获得的结果。在两个表中的 “手工调整” 列显示了咱们手工调整了命令行选项,以尽可能达到最佳密度(表 1)或启动时间(表 2)后,从常规 JVM 得到的结果。“默认” 列显示使用常规 JVM 与默认选项的结果。
多租户 JVM 实现了无共享 JVM 的 1.2 倍至 4.9 倍的密度(该值根据不一样的应用而有所不一样),如表 1 所示:
表 1. 并发应用程序的最大数量
应用程序 | 描述 | 多租户 | 手工调整 | 默认 | 经过多租户 JVM 实现的提升 |
---|---|---|---|---|---|
Hello World | 打印 “HelloWorld” 而后进入睡眠状态 | 309 | 73 | 63 | 4.2 倍到 4.9 倍 |
Jetty | 启动 Jetty 并等待请求 | 34 | - | 18 | 1.9 倍 |
Tomcat | 启动 Tomcat 并等待请求 | 28 | - | 13 | 2.1 倍 |
JRuby | 启动 JRuby 并等待请求 | 32 | 26 | 15 | 1.2 倍到 2.1 倍 |
更高的密度来自关键构件的共享,这些构件包括:
Class
对象,以及能够安全地跨租户共享的堆对象(例如,实习 String
)。表 2 显示,咱们经过多租户 JVM 实现了快 1.2 倍到 6 倍的平均启动时间:
表 2. 启动时间(第一个/平均)
应用程序 | 描述 | 多租户 | 手工调整 | 默认 | 经过多租户 JVM 实现的提升 |
---|---|---|---|---|---|
Hello World | 打印 “HelloWorld” 而后进入睡眠状态 | 5709/138ms | 514/400ms | 3361/460ms | 3.3 倍 |
Jetty | 启动 Jetty 并等待请求 | 7478/2116ms | - | 6296/12624ms | 6 倍 |
Tomcat | 启动 Tomcat 并等待请求 | 9333/6005ms | - | 7802/7432ms | 1.2 倍 |
JRuby | 启动 JRuby 并等待请求 | 12391/3277ms | 14847/4101ms | 7849/6058ms | 1.25 倍到 1.8 倍 |
从 表 2 中能够看到,多租户 JVM 上的第一个应用程序实例的启动时间一般比标准 JVM 上的慢。这是预料之中的结果,由于多租户扩展会形成额外的路径长度,致使第一个实例出现一些额外的启动延时。后续实例的启动时间始终是多租户 JVM 表现更好一些。
这些早期的结果是由于使用开发 JVM 而产生的,有可能会得到更大的改进。此外,这些示例并不影响当应用程序在不一样时间须要资源时进行的共享。在一个典型的 JVM 中,在整个生命周期中,每一个 JVM 须要的内存占用每每会不断增大。在标准 JVM 中,这种内存占用不是共享的。若是使用多租户 JVM,在资源需求不重叠时,能够更容易地共享堆的内存和本机构件。
多租户 JVM 的一个目标是无需修改就能运行全部 Java 应用程序。这在目前是不可能实现的,缘由是 Java 规范方面的一些限制以及咱们目前的实现中的一些限制。关键的已知限制包括:
本文介绍了多租户 JVM、如何使用它,以及使用它的成本和收益。咱们但愿本文激起了您的兴趣,而且但愿您会试用 beta,并向咱们提供反馈。咱们相信,多租户 JVM 能够为适当的环境提供明显的好处。
相关主题