《大规模Java平台虚拟化与调优》——1.3 大规模Java平台的技术因素-阿里云开发者社区

开发者社区> 华章出版社> 正文

《大规模Java平台虚拟化与调优》——1.3 大规模Java平台的技术因素

简介:

本节书摘来自华章计算机《大规模Java平台虚拟化与调优》一书中的第1章,第1.3节,作者:(美)Emad BenjaminLiang) 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

1.3 大规模Java平台的技术因素

当设计大规模Java平台时,需要考虑很多的技术因素。例如,对于构建良好的大规模Java平台来说,需要很好地理解Java垃圾回收(garbage collection,GC)以及JVM架构、硬件和hypervisor架构。本节中,概要讨论了GC、非一致内存架构(Non-Uniform Memory Architecture,NUMA),以及在理论和实际操作中的内存限制。稍后的章节会给出更为详细的描述,但首先在整体上理解围绕大规模Java平台设计有哪些问题是非常必要的。
1.3.1 Java平台在理论和实际中的限制
图1-1展现了Java负载在理论和实际中的规模限制,当对JVM负载进行规模划分时,这些关键的限制条件是需要谨记的。
需要强调的很重要的一点就是,JVM的理论限制是16艾字节(exabyte)。但是,并没有实际的系统能够提供这么大数量的内存。所以,我们将其视为第一个理论限制。
第二个限制是Guest操作系统所能支持的内存量。在大多数场景下,能够达到多个TB(terabyte),这取决于所使用的操作系统。
第三个限制是ESXi 5中每个VM中最多有1TB的RAM,对于我们在客户那里所遇到的任何负载这都是足够的。
第四个限制(实际上也是第一个实际的限制)就是典型的ESX服务器上可用的RAM数量。我们发现, vSphere主机平均来讲会有128~144GB,最多可以达到196~256GB。当然从可行性的角度来看,硬性的限制可能在256GB左右。我们当然也会有更大的基于RAM的vSphere主机,如384GB~1TB。但是,这类主机可能更适合于第2类的内存数据库工作负载和传统的关系型数据库管理系统(relational database management system,RDBMS),它们会使用到如此巨大的计算资源。这些系统需要如此大的vSphere主机的主要原因是大多数(稍微有一些是例外的,如Oracle RAC)传统关系型数据库不会横向扩展(scale out),而会纵向扩展(scale up)。在第1类和第2类Java平台的场景中,横向扩展是一种可行的方式,因此选择更为划算的vSphere主机是可以承受的。在第1类的Java负载中,你应当考虑让vSphere主机有一个更为合理的RAM,它的范围应该在128GB以内。
第五个限制就是服务器RAM的总量以及如何将其划分为多个NUMA节点,每个处理器插槽(processor socket)都会是一个NUMA节点,节点具有NUMA本地内存。NUMA本地内存可以用服务器RAM的总量除以处理器插槽的数量计算得到。我们知道为了获得最佳的性能,你应该在NUMA节点的内存边界范围内确定VM的大小。毫无疑问,ESX提供了很多的NUMA优化选项,但是最好始终要做到NUMA本地访问。例如,在ESX主机中,两个处理器插槽一共具有256GB的RAM(也就是说,它具有两个NUMA节点,每个NUMA节点具有128GB(256GB/2)的RAM),这表明,当你确定VM的规模时,它不应该超过128GB,因为这样就能实现NUMA的本地访问。
在图1-1中展现的限制因素能够帮助你在确定大型JVM规模时,如何做出务实且可行的设计和划分决策。但是在确定大型JVM规模时,还有其他的考量因素,如GC调优的复杂性以及维护大型JVM所需的知识。实际上,我们客户群体中绝大多数JVM都是使用4GB RAM左右的典型企业级Web应用,也就是本书中所述的第1类工作负载。但是,更大的JVM也是存在的,我们有的客户在JVM上运行大规模的监控系统和大型的分布式数据平台(内存数据库),其规模在4~128GB。对于像vFabric GemFire和SQLFire这样的内存数据


be773e20be1c6db3fd68013a5a08cc5bb5d8aed7

库同样如此,在这里,集群中单个JVM成员可以达到128GB,而整个集群的规模可以达到1~3TB。这样大型的JVM就需要更多关于GC调优的知识。在VMware,过去的几年中,尽管物理机上的GC调优与虚拟机上并没有任何差别,但我们依然为很多客户在GC优化方面提供了很多帮助。原因在于我们将vFabric Java以及vSphere的专业知识集成到了一起,这样就能帮助很多客户将运行vSphere上的Java工作负载实现最优化。当决定是否要垂直扩展JVM和VM的大小时,首先应该要考虑的是水平扩展的方式,我们发现具有水平扩展能力的平台可以获得更好的可扩展性。如果水平扩展的方案不可行,那就要考虑增加JVM内存的大小,并据此增加VM的内存。当选择通过增加堆空间/内存来加大JVM的规模时,下一个需要考量的点就是GC调优和处理大型JVM的知识。
考虑到本书所述的第三个限制,ESXi 5.1是官方的GA发布版本;但是,到本书出版之时,vSphere的一些最大限制可能会发生变化。通过官方的VMware产品文档来了解其最新的最大值。同时要注意,这些VM限制使用成本不菲的硬件,会需要数量众多的vCPU,但是对于需要这种环境的场景来说,这才能够确保其价值。
如本章前文所述,在企业级领域,大型Java平台可以划分为3类。图1-2展现了各种工作负载的类型及其相对的规模。一个流行的趋势是随着JVM规模的增长,对JVM GC调优知识的需求也在不断增长。


711db311fe55260f97caac707788f22f63bd1fdb

记住以下几点是非常重要的(对应图片从左到右):
在如今的工作负载中,堆大小小于4GB的JVM最为常见。4GB是一个特殊的场景,因为在64位的JVM空间中,它默认具备32位地址指针的优势(因此会具有非常高效的内存分布机制)。这会需要一些调优,但是并不会很多。这种类型的工作负载属于本章所定义的第1类的范畴。在服务器级别的机器上,使用默认的GC算法就足够了。只有当响应时间不能满足要求时,你才需要对其进行调优。如果发生了这样的场景,你可以遵循第3章,以及第6章中所提供的GC调优指导。
第二种工作负载场景也属于前文所述的第1类,但可能面对的是组织内部重要的用户。在这种负载的应用中,我们一般会看到被大规模使用(1000~10 000的用户)的企业级Java Web应用。该类型环境的标准是GC调优以及稍大于4GB的JVM。DevOps团队通常会具有良好的GC调优知识,并且配置JVM不再使用默认的GC吞吐(throughput)收集器。在这里我们会看到对于这种类型的工作负载会使用CMS(concurrent mark and sweep)GC算法,从而为用户提供更短的响应时间。CMS GC算法由Oracle JVM(也就是之前的Sun JVM)所提供。关于Oracle JVM或IBM JVM中其他GC算法的相关细节和信息,请参阅第3章和第6章。
第三种工作负载的场景可以划分到前文所述的第2类,但它是第2类中的一个特殊场景,因为应用程序有时候会因为不能进行水平扩展,而使用更大的JVM。如本章前文所述,一般来讲第2类的工作负载通常会是内存数据库。在这种类型中,需要深入地理解JVM GC调优的知识。你的DevOps团队必须能够清晰区分不同的GC收集器并选择最适合提高吞吐量的收集器(吞吐收集器)(与之相对的是对于延迟性敏感的工作负载应用,则要使用CMS GC以获得更好的响应时间)。
第四种工作负载的场景可以同时划分为前文所述的第2类和第3类。在这里,可能会有大型的分布式系统,客户端的企业级Java应用所使用的数据来源于后端的数据fabric,在后端会有一组或更多的内存数据库JVM节点在运行。在这种场景下,需要专家级别的GC调优能力。
除了简单地维护一个大型的JVM,你必须要了解工作负载的类型。毕竟,客户经常会垂直扩展JVM,因为他们认为这是一种简单的部署方式,最好保持已有JVM进程的现状。让我们考虑一些JVM的部署和使用场景(可能有一些存在于你目前的部署环境中,也有一些你过去可能遇到过):
某位客户最初部署了一个JVM进程。随着需要部署的应用不断增加,这位客户并没有通过增加第二个JVM和VM的方式进行水平扩展。与之相反,客户采取了垂直扩展的方式。所造成的结果就是,已有的JVM必须进行垂直扩展,以承载多个不同类型且需求各异的工作负载。
有一些工作负载,如任务调度器(job scheduler),需要高的吞吐量,而公开访问的Web应用则需要很快的响应时间。因此,如果将这些类型的应用放在同一个JVM之中,会使得GC调优的过程复杂化。当对GC进行调优以获取更高的吞吐量,所付出的代价通常是牺牲响应时间,反之亦然。
即便你可以同时实现更高的吞吐量和更好的响应时间,但这无疑会增加不必要的GC优化行为。当面临这类的部署选择时,通常最好的方式是将不同类型的Java负载划分到它们自己的JVM之中。一种方式就是将运行任务调度器的工作负载放到自己的JVM和VM中(对基于Web的Java应用同样如此)。
在图1-3中,JVM-1部署到一个VM上,这个JVM中具有混合的应用负载类型,当试图纵向扩展JVM-2中的应用时,GC调优和扩展性都会变得更为复杂。更好的方式是将Web应用划分到JVM-3中,而任务调度器划分到JVM-4中(也就是,水平扩展,并且在必要时具备垂直扩展的灵活性)。如果将JVM-3和JVM-4的垂直可扩展性与JVM-2的垂直可扩展性进行对比,你会发现JVM-3和JVM-4可扩展性更好并且更容易进行调优。
1.3.2 NUMA
非一致内存架构(Non-Uniform Memory Architecture,NUMA)是一种用于多处理器环境的计算机内存设计,在这种环境下,内存的访问时间取决于内存相对于处理器的位置。在NUMA中,处理器访问本地内存要快于访问非本地内存(也就是,其他处理器的本地内存或处理器共享的内存)。

3a88bca7231c3ea7a894289a09fe1682e770da54

理解NUMA的边界对于确定VM和JVM的大小是非常重要的。理想情况下,VM的大小应该限制在NUMA边界之内。图1-4展现了一个两插槽的vSphere主机,因此会有两个NUMA节点。图中所展现的工作负载是两个vFabric SQLFire的VM,每个VM的内存和


bf0ed83adb437956ca21a6b93286ff576f26f77b

CPU的规模都调整在了NUMA节点的边界之内。如果某一个VM的大小超出了NUMA边界,它可能会与其他的NUMA节点产生内存交错(interleave),以满足额外内存的请求,因为本地NUMA节点无法提供这些内存。图中使用红色箭头标示了内存交错(虚线箭头展示了这种交错),以此强调这种类型的内存交错是应当避免的,因为它会严重影响性能。
为了计算每个NUMA节点中可用的RAM数量,可以使用公式1-1中的等式。

NUMA本地内存=服务器上的RAM总量/插槽数

公式1-1 每个NUMA节点的RAM大小(NUMA本地内存)
例如,如果一个服务器配置了128GB的RAM,并且具有两个插槽(如图1-4所示),这表明每个NUMA的RAM是128/2,也就是64GB。这并不完全准确,因为还需要考虑ESX的消耗。所以,一个更为精确的估算结果可以由公式1-2得出。这个公式中考虑到了ESXi的内存消耗(不管服务器的规模大小,始终是1GB的常量)以及1%的VM内存消耗,也就是总可用内存的1%。这是一个保守的近似公式,每个VM和工作负载会略有不同,但是这个近似值非常接近于最坏的场景。

NUMA本地内存=[主机上的RAM总量-{(主机上的RAM总量*nVMs*0.01)+1GB}]/插槽数

公式1-2 基于ESXi的消耗进行调整后,每个NUMA节点的RAM(NUMA本地内存)
以下描述了公式的不同组成部分:

  • NUMA本地内存:实现最优内存吞吐和查找定位的本地NUMA内存量,已经考虑到了VM和ESXi 的消耗。
  • 主机上的RAM总量:物理服务器上所配置的物理RAM数量。
  • nVMs:在vSphere主机上所规划部署的VM数量。
  • 1GB:运行ESXi所需要的内存消耗。
  • 插槽数:物理服务器上可用的插槽数,2个插槽或4个插槽。

公式1-2假设的是最为悲观的开销范围,尤其当VM的数量增加的时候,显然你增加的VM越多,你就会有更多的消耗。对于较少数量VM的场景,公式1-2的近似值是相当准确的。同时,公式中假设的是没有过量使用内存的情况。这个公式的结果对更大的VM更为有利,在这种情况下,NUMA是最重要的考量内容。当划分为更大的VM时,一般来讲你会维护更少数量要配置的VM,所以这个开销公式完全适用。实际上,对较大的VM来讲,它们具有依赖内存的工作负载,其最佳的配置是每个NUMA节点对应一个VM。如果你将这个公式应用到超过6个VM的部署环境,比如说10个VM,那么这个公式会过高估算所需的消耗量。更为精确的做法是,你可以使用6%规则,也就是不管VM的数量多少,总是假设6%的内存消耗是足够的,而不用关心是10个VM还是20个VM。
如果你没有时间来了解公式,而是想快速开始进行配置,那么可以将内存的大约6%作为消耗。有很多的场景,并不是所有的计算方式都会被用到。例如:
样例1—使用6%估算方式:这表明如果你有一个具备128GB物理RAM的服务器(双插槽的主机,每个插槽上有8个核心),并且在配置主机上的2个VM时,采用6%消耗的计算方式,那么每个VM总的NUMA本地内存将会是=> ((128 0.94) – 1) / 2 => 59.7GB。因为有2个VM,那么提供给这2个VM的内存大约是59.7 2 => 119.32GB。
你也可以采用公式1-2的方式,如下面的样例2所示:
样例2—使用公式1-2来计算NUMA本地可用内存:同样,我们假设有一个具有双插槽(每个插槽上有8个核心)且128GB的主机,要在上面配置2个VM,NUMA本地内存= (128-(128 2 0.01)-1)/ 2 => 62.22GB。注意,这是针对2个VM进行的计算。假设你想配置16个具有单个vCPU(1vCPU = 1个核心)的VM,那么NUMA本地内存=(128-(128 16 0.01)-1) / 2 => 53.26GB。这很可能过于保守了,更为精确的应该是6%消耗估算方式。
对于最佳实践,估算消耗最好的方式是总物理RAM的6%(再加上ESXi所需的1GB),也就是如样例1所示。
在前面的例子中,展现的计算过程是基于一个具有128GB RAM的服务器,真正的本地内存可能是((128 * 0.99)-1GB) / 2 => 62.86GB,这是你可以配置的最大的VM。在这种场景下,你可以非常安全地配置2个具有62.68GB RAM的VM,每个VM具有8个vCPU,因为每个VM都会部署到一个NUMA节点上。还有另外一种可行的选择,如果你愿意部署更小的VM,那么你可以部署4个VM。每个VM具有62.86GB / 2 => 31.43GB的RAM以及4个vCPU,NUMA的调度算法依然会将VM放置到本地NUMA节点之上。
在超线程(hyperthreaded)的系统中,VM所具有的vCPU数量大于NUMA节点内物理核心的数量,但是会小于逻辑处理器的数量(逻辑处理器通常是物理核心数量的2倍,但更为实际的做法是,让逻辑处理器的数量是物理核心的1.25倍),每个物理NUMA节点上的这些VM可能会从使用本地内存的逻辑处理器中受益,而不是全部的核心都使用远程的内存
为了进一步详细阐述ESXi NUMA调度算法,图1-5展现了具有双插槽且每个插槽6核心的服务器。


<a href=https://yqfile.alicdn.com/c73b33529ed1ac6b0ac86bdcc659e90ea6d1bfce.png" >

在这个图中,最初有4个VM,每个VM有2个vCPU以及大约20GB的RAM。最初的ESXi调度算法将会遵循轮询(round-robin)的模式。首先,会发生第一步(如图中带有数字1的黑色圆圈),然后,接下来2个vCPU的VM会在另一个可用的空NUMA节点上调度,随后同样的方式(第三步和第四步)调度第三个和第四个VM。此时,4个2vCPU 20GB VM都已经调度过了,调度的结果就是4个VM将会占用每个插槽的4个核心,如图中的红针所示(红针就是最初ESXi调度4个2vCPU VM的结果)。稍后,部署了第五个VM,它具有4vCPU以及40GB RAM,现在,ESXi会试图在一个NUMA节点上调度这个VM。这是因为这个VM是4vCPU,并不会被认为是跨NUMA节点的VM,所以它的全部4个vCPU会被安排在一个NUMA节点上,尽管此时只有2个vCPU是可用的。按照NUMA的平衡感知算法(balancing awareness algorithm),可能会发生的事情是:ESXi调度器将会最终强制要求其中一个2vCPU VM迁移到另外一个NUMA节点上,从而试图将第五个4vCPU VM放置到一个NUMA节点上。ESXi调度器采用这种行为是因为它使用一种被称为NUMA客户端的理念,会以每个NUMA客户端的方式调度VM,在这里NUMA客户端的默认大小就是物理NUMA节点的大小。在本例中,默认值就是6,所以任何6vCPU或更小的VM将会调度到一个NUMA节点上,因为它属于一个NUMA客户端。如果你想改变这种行为,应该强制要求NUMA客户端的计算更为细粒度。NUMA客户端的计算是通过numa.vcpu.maxPerClient控制的,它可以通过Advanced Host Attributes -> Advanced Virtual NUMA Attributes进行设置,如果你将其值修改为2,那么在我们的样例中,每个插槽会有3个NUMA客户端,每个2vCPU的VM将会调度到一个NUMA客户端之中,而第五个4vCPU VM的调度将会跨2个NUMA客户端,如果需要的话,它可能会跨2个插槽。你很少需要进行这种级别的调优,但是这个例子阐述了vSphere中NUMA算法的强大之处,在这一点上远超过任何非虚拟化的Java平台。
通常,当虚拟机启动时,ESXi会为其分配一个home节点,这是其初始安置(initial placement)算法的一部分。虚拟机只能运行其home节点上的处理器,它新分配的内存也来自于home节点。除非虚拟机的home节点发生了变更,否则它只会使用本地内存,这就避免了远程访问其他NUMA节点所带来的性能损耗。当虚拟机启动时,它会分配一个初始home节点,这样NUMA节点间整体的CPU和内存负载就能保持平衡。鉴于在大型NUMA系统中,跨节点所造成的延迟会有很大的差异,因此ESXi会在启动的时候来确定这些节点间的延迟,并且在安置大于NVMA节点的虚拟机时会用到这些信息。这些更大的虚拟机放置到多个NUMA节点上,这些节点彼此接近,从而达到最低的内存访问延迟。只进行初始安置时设置(initial placement-only)的方式对于只运行一个工作负载的系统来说足够了,比如基准配置在系统的运行过程中保持不变的情况。但是,对于数据中心级别的系统来说,这种方式无法保证达到好的性能和公平性,因为这种级别的系统要支持工作负载的修改。因此,除了初始化安置过程以外,ESXi 5.0确实也提供了在NUMA节点间动态迁移虚拟CPU和内存的功能,从而提高CPU的平衡性并增强内存的本地化。ESXi结合了传统的初始安置方式以及动态重平衡的算法。系统会阶段性(默认每两秒钟)检查各种节点的负载并确定是否要通过将虚拟机从一个节点迁移到另一个节点以实现负载的重平衡。
这个计算会考量到虚拟机的资源设置和资源池来提升性能,而不会违反公平性和资源的权限设置。重平衡器会选择合适的虚拟机并将其home节点修改为负载最小的节点。如果它能做到这一点,重平衡器会将已经具有一些内存的虚拟机转移到目标节点上。从这一刻开始,虚拟机会在其新的home节点上分配内存,虚拟机的运行也会依赖于新home节点中的处理器。重平衡是维护公平性并确保所有节点完全使用的有效方案。重平衡器可能需要将虚拟机迁移到一个分配了很少甚至没有内存的节点上。在这种情况下,虚拟机会引起性能损耗,这与大量的远程内存访问有关。ESXi可以消除这种损耗,这是通过透明地将内存从虚拟机的原始节点迁移到新home节点实现的。
在vSphere 4.1/ESXi 4.1中,hypervisor并没有将底层的物理NUMA架构暴露给操作系统,因此运行在这种VM上的应用程序工作负载并不能充分利用额外的NUMA挂钩(hook)所提供的优势。但是在vSphere5中,引入了vNUMA的理念,通过配置你可以暴露底层的NUMA架构给操作系统,所以能够感知NUMA的应用就能充分使用它了。在Java中,可以使用-XX:+UseNUMA这个JVM参数,但是,它只兼容于吞吐型的GC,而不兼容CMS GC。矛盾的是,在大多数内存敏感的应用中,NUMA是重要的因素,而延迟敏感也是很大的考量因素,因此CMS收集器更为合适。这表明你不能同时使用CMS与-XX:+UseNUMA可选项。而好消息是vSphere NUMA算法对于提供本地化来讲已经足够好了,尤其是当你遵循NUMA规模划分最佳实践时更是如此—如在内存和vCPU方面,使VM的规模大小适应NUMA的边界。
1.3.3 在生产环境中,最为常见的JVM规模
前面已经讨论了你可以部署的各种JVM规模(在一些场景中,是非常大的JVM),但是要记住的很重要的一点就是,在数据中心里面,堆大小为4GB的JVM最为常见。这可能是相当繁忙的JVM,具有100~250个并发线程(实际上的线程数可能差异很大,因为这取决于工作负载的特性),4GB的堆大小,那么JVM进程大约是4.5GB,再加上0.5GB的Guest操作系统,因此该VM推荐的预留内存是5GB,这个VM具有两个vCPU且只包含一个JVM进程,如图1-6所示。


<a href=https://yqfile.alicdn.com/dec7d5724a05e7cd56543600c27a691adf276a07.png" >

1.3.4 JVM和VM的水平扩展与垂直扩展
当考虑水平扩展与垂直扩展时,你可以有3种可选方案,如图1-7所示。
下面的章节会详细介绍这3种方案的利弊。
方案1
在方案1中,JVM引入Java平台中是通过创建新的VM并在上面部署新的JVM实现的(因此,横向扩展VM和JVM的模型)。
方案1的优势
这种方案提供了最佳的可扩展性,因为VM和JVM会作为一个整体单元被ESXi调度器所调度。实际上ESXi调度的是VM,但因为这个VM上只有一个JVM,所以造成的实际效果就是VM和JVM会作为一个整体进行调度。

32a136e3727510d10ee5859a4f3e990d9e113094

这种方案也提供了最佳的灵活性,能够独立地关闭任何VM和JVM而不会影响Java平台的其他部分。但毫无疑问这是相对而言,因为大多数Java平台都是可水平扩展的,即便JVM实例被关闭,也会有足够的实例来服务于访问流量。更多的实例会有更好的可扩展性,这种相对的比较基于完全相同的系统使用100个JVM和VM还是150个JVM和VM。对某一个特定实例,如果你正在比较和对比平台设计方案并试图在使用100个JVM还是150个JVM中做出选择,不管是100个还是150个JVM都具有相应的RAM总量。很明显,具有150个JVM的系统具有更好的灵活性和可扩展性。在150个JVM的场景中,因为你有更多的JVM,因此相对于100个JVM的情况,JVM可能会更小。在这种情况下,如果150个JVM平台中的某一个JVM遇到了问题,所造成的影响可能会更小,因为这个JVM所持有的数据比100个JVM场景所持有的数据更少。因此,150个JVM横向扩展的健壮性使其成为更为合适的可选方案。
如果系统进行了精化(refined),之前所提到的水平可扩展的优势就能得以发挥了。这里的精化指的是基于64位的架构采用VM和JVM的最佳实践,JVM具有较为合理的大小,也就是大致4GB的最小堆空间,而不是围绕着32位JVM所形成的碎片化的1GB堆空间(有一些遗留的32位JVM能够大于1GB,但是对于实际使用来讲,32位JVM会有一个遗留的1GB限制)。
方案1的劣势
这种方案的成本会更为昂贵,因为它会导致更多的操作系统副本,许可证费用很快就会变得更为高昂。管理这种系统的成本也会更高,因为需要监控更多的VM和JVM。
并没有什么技术原因强制要求你每个VM上面只放一个JVM。唯一的例外情况是内存数据库系统(如第2类工作负载),这类系统需要从本地NUMA节点上获得高吞吐量的内存。在这种情况下,VM的大小需要进行调整以便安装在一个NUMA节点之内并且上面只安装一个JVM。另外,还要注意内存数据库中的JVM通常会相当大,有时会达到128GB,这与第1类工作负载中JVM的大小(一般是1~4GB的堆空间)截然不同。方案1对于第1类工作负载(本章前文进行了定义)是很重要的,但是你也会有很多的机会去合并JVM,从而消除浪费的JVM和VM实例。
对遗留的32位JVM来说,这是一种通用的模式,在这种情况下,32位JVM的1GB限制会要求Java平台的工程师安装更多的JVM实例,以处理不断增长的请求流量。这里的缺点在于你需要支付额外的CPU/许可费用。如果你将JVM迁移为64位,同时增加堆空间的大小,你可以用更少的JVM服务于相同数量的网络流量,因此会带来成本的节省。当然,JVM的大小会随着增长,比如从1GB增长到4GB。
方案2
方案2涉及通过合并较小的JVM纵向扩展JVM的堆大小,同时也会形成合并的VM。
方案2的优势

  • 采用方案2的优势如下:
  • 因为JVM和VM的数量更少,所以减少了管理的成本。
  • 因为操作系统副本的数量会更少,所以降低了许可证的费用。
  • 因为更多的事务(更可能)在同一个堆空间内执行,而不用跨网络访问其他JVM时所需的排列(marshaling),所以改善了响应时间。
  • 降低了硬件的成本。

如果你查看一下图1-7中的方案2,它展示的2个JVM(JVM-1A与JVM-2A)是由方案1中的4个JVM(JVM-1、2、3和4)合并而成。在这个过程中,如图所示,4个VM也被合并成了2个VM。例如,如果JVM-1、2、3和4的堆大小都是2GB,每个JVM都运行在具有2vCPU的VM上,这意味着所有JVM中服务于堆的RAM也就是服务于应用的RAM总和是8GB。所有VM的vCPU数量是8。现在,合并为2个VM和2个JVM时,在方案2中JVM(JVM-1A与JVM-2A)每个都是4GB的堆空间,总计是8GB,每个VM上有2个vCPU。这表明2个VM上一共有4个vCPU,节省了4个vCPU,因为在最初的方案1中,4个VM都具有2个vCPU。
还可能减少vCPU,同时依然保持相等数量的RAM(JVM堆空间),因为对于更大的JVM堆空间,GC可以非常好地进行垂直扩展而不会过多消耗CPU。这很大程度上依赖于工作负载的行为,有一些工作负载确实会随着JVM的扩展而增大CPU的使用。但是,大多数第1类工作负载表现出的行为是当合并为更大的JVM堆时,可以释放不必要的vCPU。64位JVM是功能很强的运行时容器,尽管启动时会有一些初始成本,但是它们的确可以在更大的堆空间中处理大量的事务。当你考虑创建新的JVM时,同时你会问是否要创建新VM这样的问题。如果有人需要添加新的VM,vSphere管理员通常会问为何需要它。因为JVM是具有较强能力的机器(正如VM是具有很强能力的计算资源一样),vSphere管理员和DevOps工程师通常都会仔细检查创建新的JVM是否是必要的(而不是采纳合理地利用已有的JVM实例,为其增加堆空间以应对更大的流量的方案)。
方案2的劣势
使用方案2的劣势如下:

  • 因为使用了更大的JVM,如果发生JVM崩溃而没有恰当地进行冗余或持久化事务,你会有失去更多数据的风险(相对于方案1中的更小的JVM)。
  • 因为合并,可能会有更少高可用性(high-availability,HA)的JVM实例。
  • 合并会局限于业务线。你并不想将来自不同业务线的应用混合到同一个JVM之中。如果你将两个业务线的应用放到了同一个JVM中,那么如果这个JVM崩溃,将会同时影响到这两个业务线。
  • 更大的JVM可能会需要更多的GC调优。

方案3
如果方案1和方案2都是不可行的,那么考虑方案3。在这种情况下,你在一个更大的VM上部署了多个JVM。现在,JVM-1B和JVM-2B可以是合并后的JVM副本,如方案2中的那样,也可以是没有合并前的副本,如方案1中那样。不管是哪种情况,你都可以将这些JVM堆积(stack)到一个或多个更大的VM之中。
方案3的优势
使用方案3的优势如下:

  • 如果当前的平台与方案1类似,那么这种方案可能会有一定的优势,鉴于维护的原因,保持当前数量的JVM在部署环境中的完整性,然后再去考虑更大的VM,让多个JVM堆积到上面。
  • 减少操作系统许可证的数量。
  • 减少VM实例的数量。
  • 因为更少的VM,所以减少了管理成本。
  • 你可以为每个业务线构建专用的JVM,也可以部署多个业务线的JVM到同一个VM上。只有VM合并的成本节省大于某一个VM崩溃给多个业务线所带来的影响时,你才应该选择这种方案。
  • 大的VM可以为JVM配置更多的vCPU。如果VM上有两个来自不同业务线的JVM,例如,它们的高峰期在不同的时间段,那么繁忙的JVM就有可能使用所有的vCPU,当另一个JVM到达访问高峰时也会发生类似的事情。

方案3的劣势
较大的VM很可能是必需的,但是相对于小的VM,调度更大的VM需要更多的调优。
各种性能案例显示,对于第1类工作负载来说,最恰当的VM规模是2~4个vCPU,而对于第2类的工作负载,会需要多于4个vCPU,或者说最少4个vCPU。不过,从HA的角度来看,会消除调度的机会。但需要记住的是,作为内存数据库的第2类工作负载很可能需要容错、冗余以及磁盘持久化,因此不会那么依赖于VMware HA以及自动化的分布式资源调度(Distributed Resource Scheduler,DRS)。
因为这种方案会试图合并VM,那么很可能不同业务线的JVM会部署到相同的VM之中。你必须要正确地对其进行管理,因为无意中对某个虚拟机进行重启可能会影响到多个业务线。
你可以将JVM进行合并,然后将其堆积到同一个VM上。但是,这就要求JVM要足够大才能完全使用底层的内存。如果你配置少量的更大的VM,那就意味着你的VM会有更多来自于底层硬件的RAM,为了充分使用这些RAM,你可能会需要更大的JVM堆空间。因为JVM的规模更大一些,所以当JVM崩溃时,如果你没有进行适当冗余和持久化事务,那么你会失去更多的数据。
这种方案可能会需要大型的vSphere主机和更大的服务器,因此成本会更高。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:

华章出版社

官方博客
官网链接