大数据,机器学习,人工智能大数据玩转大数据

杂谈:我的JAR有什么问题? 为什么停止构建胖JAR

2019-02-08  本文已影响3人  老老戟
图片.png

HuBSPOT的后端服务几乎都是用Java编写的。我们有1000多个微服务在不断地构建和部署。当部署和运行一个Java应用程序时,它的依赖项必须出现在类路径上才能运行。以前,我们通过使用maven-shade插件构建一个胖罐子来处理这个问题。这将获取应用程序及其所有依赖项,并将它们打包到一个巨大的JAR中。这个JAR是不可变的,没有外部依赖关系,这使得它易于部署和运行。多年来,这是我们打包所有Java应用程序的方式,它运行得很好,但是它有一些严重的缺点。

胖JAR

我们遇到的第一个问题是jar不能像这样聚合。在多个jar中可能存在具有相同路径的文件,默认情况下,shade插件包含fat jar中的第一个文件并丢弃其余文件。这导致了一些非常令人沮丧的错误,直到我们知道发生了什么(例如,Jersey使用META-INF/Services文件来自动发现提供者,这导致一些提供者没有注册)。幸运的是,shade插件支持资源转换器,允许您在遇到重复文件时定义合并策略,以便我们能够解决此问题。然而,它仍然是一个额外的“gotcha”,我们所有的开发人员都需要意识到这一点。

我们遇到的另一个更大的问题是,这个过程缓慢且效率低下。以我们的一个应用程序为例,它包含70个类文件,当打包为JAR时,总大小为210KB。但是在运行了shade插件来捆绑其依赖项之后,我们最终得到了一个包含101481个文件的胖jar,它的重量为158MB。将100000个小文件合并到一个存档中很慢。在构建结束时将这个jar上传到S3是很慢的。在部署时下载这个JAR的速度很慢(如果我们有很多并发部署的话,它会使应用服务器上的网卡饱和)。

由于有超过100名工程师不断的投入,我们通常每天进行1000-2000次构建。随着这些构建中的每一个都上传一个胖jar,我们每天生成50-100GB的构建工件。最痛苦的部分是每件文物之间有多少重复。我们的应用程序在第三方库方面有很多重叠,例如它们都使用guice、jackson、guava、logback等。想象一下,我们在S3中有多少个这些库的副本!

找到更好的方法

最后,我们决定要找到一种更好的方法来做到这一点。另一种选择是使用maven依赖插件将应用程序的所有依赖项复制到构建目录中。然后,当我们整理构建文件夹并将其上载到S3时,它将包含所有依赖项,因此我们仍然拥有我们想要的不可变构建。这节省了我们运行shade插件的时间和它增加的复杂性。但是,它并没有减少构建工件的大小,因此在构建结束时上载tarball仍然需要一段时间,这也意味着我们仍然在浪费大量空间来存储这些构建工件,然后在部署时下载这些工件仍然需要很长时间。

推出Slimfast

使用之前的示例应用程序,如果我们刚刚上传了210kb jar呢?想象一下构建速度会有多快(结果是速度快了60%)。想象一下在S3中我们可以节省多少空间(超过99%)。想象一下在部署时我们可以节省多少时间和I/O。为了做到这一点,我们编写了自己的maven插件slimfast。它默认绑定到部署阶段,并将应用程序的所有依赖项分别上载到S3。实际上,这会使构建速度变慢,但诀窍是,只有当S3中不存在依赖项时,才需要这样做。而且,由于我们的应用程序的依赖性并不经常改变,所以这一步通常是禁止操作的。插件生成一个JSON文件,其中包含S3中所有依赖性工件的信息,以便我们以后可以下载它们。在部署时,我们下载了应用程序的所有依赖项,但是我们在每个应用程序服务器上缓存了这些工件,因此这个步骤通常也是一个不操作。最终的结果是,在构建时,我们只是上传应用程序的瘦jar,它只有几百千字节。在部署时,我们只需要下载这个只需几秒钟时间的薄jar。

结果

在推出之后,我们从每天生产50-100GB的构建工件发展到不到1GB。此外,不运行shade插件和不将胖jar上传到S3在构建速度方面有巨大的好处。下面是一个图表,显示了一些项目变更前后的构建时间:

图片.png

我们已经在生产中运行这个设置超过4个月了,它工作得很好。查看Slimfast自述文件,了解有关如何设置它的详细信息,并让我们知道它是如何为您工作的!

图片.png
上一篇 下一篇

猜你喜欢

热点阅读