虚拟化技术已经有了几十年的发展历史,并且在硬件、操作系统层面都已经得到了广泛的应用。虚拟化不但可以显著节省成本,而且还可以提升管理性。同样,虚拟化技术也可以应用在JVM中,以提高资源利用率,降低单应用的部署成本。早在2004年,Sun公司就提出过Java应用虚拟化的设想,并且还制定过两个JSR规范。那现在JVM虚拟化技术发展到了哪一步?基于JVM的虚拟化技术在实现过程中有哪些难点?为了回答这些问题,InfoQ采访了JVM专家李三红。
李三红:传统的Java应用部署模式,一般遵循“硬件->操作系统->JVM->Java应用”这种自底向上的部署结构,其中Java EE应用可以细化为“硬件->操作系统->JVM->JavaEE容器->JavaEE应用”的部署结构。这种部署结构往往比较重,操作系统、JVM和JavaEE容器造成的overhead很高,而很多时候一个Java应用并不需要跑满整个硬件的资源,导致这种模式的资源利用率是比较低的。
而另一方面,硬件虚拟化技术逐渐成熟:VMware Hypervisor、Xen、KVM、Power LPAR等技术能够帮助我们在同一个硬件上部署多个操作系统实例(每个操作系统实例可以理解为宿主机的租户),而时下流行的OS Container技术如LXC、Docker等,则是把操作系统虚拟化为多个实例,实现更轻量级的虚拟化。无论哪个层面的虚拟化,其目的都是对资源利用率更加高效的追求。
同样的思路,我们也可以在JVM层面,或者容器框架层面做虚拟化,类似于Hypervisor或者OS Container,让虚拟化的JVM/容器框架可以支持多租户的运行模式,这是比OS虚拟化更高一层的做法:
如上图,从左到右来看,随着虚拟化层次的提高,从Hardware到OS,到JVM,再到容器,单个租户应用的部署成本也在下降,最右边的“多租户”的JavaEE Container(Multitenant-Container)是在容器框架层面的虚拟化,可以支持更高密度的应用部署,而不是传统的APP : Container = 1:1的部署方式。
什么是单个租户应用部署成本?举一个简单的例子,在没有虚拟化之前,传统的部署模式,一个JavaEE应用需要独占所有的硬件资源(CPU、MEM、网络等)。Hypervisor的出现,使得一个共享的硬件资源上可以同时跑多个OS,这种资源使用上的节约本质上是通过Over-Commit(即允许租户超量使用物理资源)来达到的,即我们假设跑在同一个虚拟化环境的不同租户不会在同一个时间消费同样的资源。同样的道理可以来理解JVM/容器框架的虚拟化。
多个JavaEE应用可以部署在同一个JVM/容器内,但逻辑上它们各自会认为是运行在一个独立的JVM/容器内,从而可以更大程度的提高资源利用率,降低单应用的部署成本。
李三红:早在2004年,Sun公司就提出过JVM虚拟化这方面的想法,当时Grzegorz Czajkowski领导了一个叫做巴塞罗那的研究项目,该项目基于Java HotSpot虚拟1.5版本开发了Multi-Tasking Virtual Machine(MVM)。MVM旨在提高Java程序的启动速度,节省内存开销。不过自从Sun被甲骨文收购后,我们没有听到关于该项目的任何新的进展。尽管没有看到MVM成功产品化,不过它却留下两个重要的JSR(Java Specification Request)规范:JSR121和JSR284。JSR284目前在java.net上有一个实现它的孵化项目jsr284-ri-tck(ri: Reference Implementation, tck: Test Compatible Kit)。
从2009年开始,IBM Java团队也开始着手研究JVM的虚拟化技术,并于2013年发布第一个基于IBM J9 虚拟机的Bata版本。IBM Multi-tenant JVM是JVM层面的虚拟化,其思路是把多个标准的Java应用运行在同一个JVM上,让这些应用共享底层的GC、JIT、Java运行时库等基础组件。Waratek也在2012年的Red Hat技术峰会上发布了CloudVM Beta版本,支持多租户。与IBM Multi-tenant JVM类似,Waratek允许多个应用运行在同一个CloudVM上,每一个应用运行在一个叫Java Virtual Container(JVC)的容器里。
李三红:虚拟化的本质就是把本来独占的资源共享出来,即在A不需要的时候,B可以拿来用,从而提高资源的利用率。
如果我们能把独占资源的应用,通过JVM的虚拟化的能力,同时部署在同一个“多租户”JVM里,或者是同一个“多租户”容器里, CPU、Heap这些资源就可以极大程度地共享了,部署的密度也就可以成倍的提高,其结果就是单应用的部署成本成倍的下降。打个简单的比喻,如果按照1:1的部署密度,机器的成本是1000台,如果我们能把部署的密度提高到1:10,机器的成本就是1000/10=100台。
多租户的JVM或者容器,其最大的应用场景就是高密度的应用部署场景。也许有人有疑问,OS虚拟化也可以提高部署密度, 为什么要选择JVM虚拟化?
我们就拿Docker作为一个例子来比较一下。比如利用Docker可以把运行在同一个实际操作系统上的单个JVM进程互相隔离,每个JVM进程可以看作是实际操作系统的一个租户,下面是一个简化的例子(先不考虑复杂的OS内存交换场景):
左图表示在OS之上,使用Docker虚拟出来两个租户,即两个独立的JVM进程分别运行在两个隔离的Docker容器里。右图则表示JVM/容器的虚拟化方案。在Docker的方案里,由于两个App1和App2是跨JVM进程的,所以Heap资源对于它们来说不是共享的,很难在Heap内存在App1不使用的时候,可以拿来给App2使用,这个就是一个限制Docker这种方案部署密度的因素,比如你有8G内存,你可能会选择部署两个Docker容器,每个4G。而这个因素对于JVM虚拟化方案,则不存在这样的限制,因为所有的App都是在一个进程里的,Heap对大家来说都是共享的,完全可以通过合理的安排选择部署更多的App。
李三红:在IBM的时候,其实我们内部有过很多次的讨论。如果我们从虚拟化的这个角度来看,从硬件, 到OS, 再到JVM, 以及容器, JVM的虚拟化(多租户JVM)以及容器的虚拟化(多租户容器)是一个技术的发展趋势。但是,现实的情况是,根据我们这些年在这个领域的经验与积累,基于传统的JVM实现JVM虚拟化,技术上碰到的很多的挑战,其中一些的确难于在短期内找到一个好的解决方案。(关于里面的一些实现细节,读者可以参考我之前的文章, 高密度Java应用部署的一些实践 ) 。简单列举一个例子,在多租户JVM中,每一个租户都有自己独立的Region, 但是如果租户与租户之间出现交叉引用的情况,就会导致object leakage,这个是我们内部使用的一个技术术语。其结果是,在某些条件下,某个租户退出Region不能合理释放。这个问题头痛的地方在于,每次Java class library的代码改动,都有可能造成新的object leakage。类似这样的问题, 需要Java Class Library开发者和多租户JVM之间有一种约定,或者范式来共同约束解决这类问题。
我们再来谈第二个问题。 就像你说的,目前做多租户JVM的厂商的确很少,事实上本来做商业JVM的厂商也就那么几家。IBM J9是比较早切入这个领域的JVM,主要是由中国和加拿大的团队一起开发, IBM 也只是做了JVM这层的虚拟化,并没有支持到容器级别。不过据我所知, 这个项目已经停了, 停的原因可能很多,我个人认为主要的原因是: 第一,IBM没有真实的应用场景来检视高密度部署的实际价值。第二,资源和优先级上的考虑,Java技术中心有更重要的东西来做。不过到目前为止,IBM好像还没有在正式场合公布这个东西。
如果我们广义上来谈JVM虚拟化,而不要局限于多租户JVM(即一个JVM上跑多个Java应用的场景), 我个人觉得JVM虚拟化技术在高密度部署领域是有非常大的价值,尤其是在云计算大行其道的当今, 我想这也是Waratek一直所信奉的。 这里谈到应用的前景,我想有两个问题值得再深入考虑: 第一,在云计算的环境下为JVM虚拟化找到高密度部署的应用场景,单个租户应用部署成本是关键的考量因素。第二,基于特定的场景,规避一些技术实现上难点。
李三红:James Gosling带领团队在1991年开始了一个叫”Oak”的项目,这个就是Java的前身。1995年,Java1.0发布。“Write once, run anywhere” 这句Java口号想必大家耳熟能详。Java刚开始出现的时候主要面向Interactive Television领域,直至后来几年的发展,SUN一度想用Java来打造桌面的网络操作系统,取代当时如日中天的Windows。不过不曾想,Java虽未在Embedded、Desktop领域内取得多大的建树,出乎意料地,却在企业级应用领域开花结果,占据了如今几乎统治的地位。失之東隅,却收之桑榆。
Sun在2006年的Java One 大会上,宣布Java技术开源,随后年底的时候在GPL协议下发布HotSpot以及javac,这是Java发展中的里程碑事件。从2006到2010年,这期间SUN一路磕磕绊绊,尽管手握Java这个金矿,但在商业上一直找不到好的盈利点,直到2010被Oracle收购。 2010年,也是Java发展的一个重要的分水岭,Java面临分家的风险, 一方是Apache Harmony为代表的, 其后是IBM的支持, 另一方是OpenJDK及其背后的Oracle。最后博弈的结果现在大家都知道了, IBM转向OpenJDK,Apache Harmony也结束了它的历史使命,被Apache之后束之高阁。Harmony为IBM在Java上赢得的应有的话语权,另外一个副产品,就是给移动端Android平台贡献了Java核心类库代码。 2010年是Java重生的一年。随后2011年OpenJDK 7发布, OpenJDK 7是第一个Java SE的参考实现。
李三红:好像有这么一句话,原话不太记得了,一流的公司建生态,二流的公司定标准,三流的公司卖产品。 这句话也可以用在Java上。 Java的成功,离不开逐步完善,日臻成熟的Java技术生态圈,这个是远远超出语言层面优劣的东西。 在产品的选型上,无论纵向上,还是横向上,以Java技术构建的产品都有着无比的丰富性,Apache社区的繁荣,可以作为这个故事的佐证。这个也是许多小众语言诸如Scala、JRuby愿意选择运行在JVM上的原因。
如果单从语言这个层面,谈论Java的成功,我就借用Mark Reinhold的一句总结,四个词概括Java的特征:Readability、Simplicity、 Universality和Compatibility。这也是Java语言能够风行,并被大家所接受的重要因素。
李三红,蚂蚁金服 JVM Architect,前IBM Multi-tenant JVM项目技术负责人。目前在蚂蚁金服基础技术部,负责OpenJDK/HotSpot相关的开发优化工作。 十多年的Java开发经验,2008年加入IBM,参与基于OSGi框架的安全方面的开发,2010年加入Java技术中心,参与IBM Java虚拟机 J9的开发,在Java/JVM技术的领域拥有多项专利。 在JavaOne、OSTC、IBM Technical Summit、IBM APN Summit等会议上担任演讲嘉宾,上海Java技术社区JUG(Java User Group)组织者。