2020年你将会选择哪个微服务框架?
前言
截至2020年,java仍然是构建web应用程序的最流行的编程语言之一,尽管它必须面对来自go,python和typescript等新型语言的激烈竞争。
在java世界内部,spring框架已成为微服务开发的事实上的标准,通过诸如spring boot和spring data之类的库,该框架易于使用,并且可以进行高效且大部分情况下轻松进行开发。
但是,近年来,已经引入了新的框架,声称可以缩短java应用程序的启动时间并减少其内存占用。由于我目前正在使用java开发基于微服务的大型应用程序,因此我想测试哪种java框架最适合这种架构。
因此,我的主要重点是开发的易用性以及微服务的资源消耗两个方面。
对于资源消耗方面,spring一直都被人诟病,尤其是在涉及单个流程所需的资源开销。在应用程序服务器时代,由于实例数量很少,因此这并不是主要问题。但是,随着微服务架构及其大量小型实例的兴起,这个问题变得越来越明显。正如christian lusardi最近所说的那样:
“我发现使用spring boot运行的基本java应用程序至少需要1gb的ram,开发中间件应用程序没关系,但是在微服务体系结构中,这非常糟糕!”
微服务框架介绍
1 spring
为了解决早期java enterprise的复杂性,spring于2003年应运而生。spring核心是依赖注入(di)和面向切面编程(aop),后来衍生出易于使用的spring mvc等web应用框架。通过其良好的文档,全面的各方面整合类库,spring使开发人员可以有效地创建和维护应用程序,并提供平坦的学习曲线。
spring在运行时使用反射执行di。因此,当启动spring应用程序时,将在类路径中扫描带注解的类。基于此,实例化并链接到具体对象。这种做法非常灵活且对开发人员很友好,但它可能使得启动过程缓慢并占用大量内存。另外,将这种机制迁移到graalvm非常困难,因为graalvm不支持反射。
2 micronaut
micronaut是比较新的全栈微服务框架,由grails框架的创建者于2018年引入。
micronaut提供了构建功能全面的微服务应用程序所需的所有工具。同时,它旨在提供快速启动并减少内存占用。通过使用java注解处理器执行di,创建面向切面的代理(而不是运行时)配置应用程序,可以实现此目标。
micronaut中的许多api均受spring和grails的启发。这无可厚非,毕竟这样有助于快速吸引spring及grails的开发人员。micronaut提供了诸如micronaut http,数据,安全性和各种其他技术的连接器之类的模块。但是,这些库的成熟度仍落后于spring的同类库。
3 quarkus
quarkus是red hat在2019年引入的kubernetes原生java框架。它基于microprofile,vert.x,netty和hibernate等标准构建。
quarkus的目标是通过在容器编排平台中允许更快的启动,较低的内存消耗和近乎即时的扩展来使java成为kubernetes中的领先平台。quarkus通过使用自定义的maven插件在编译时而不是在构建时执行尽可能多的工作来达到此目的(在quarkus中,这也称为编译时启动)。
quarkus使用了大多数现有的标准技术,而且还支持扩展。但是,由于该项目仅在一年之前才开始,所以这些扩展的成熟度和兼容性并不总是很清楚。随着平台的发展,这种情况将来可能会改变。
4 helidon microprofile
microprofile项目立项于2016年,与其前身jee一样,microprofile是可以由各种供应商实施的规范。到目前为止,microprofile规范已经提出了多种实现方式,最著名的是payara micro和helidon mp。
payara是从glassfish派生的jakarte ee服务器,而payara micro是其microprofile实现。helidon是oracle在2018年启动的运行时,提供了自己的microprofile规范实现。
由于它们是从jee派生的,因此microprofile规范已经很成熟并且有据可查。但是,缺少用于现代技术的连接器或替代诸如spring data和spring security之类的库的方法。
此外,由于同时开始了jakarta ee(也在eclipse foundation中)的开发,microprofile的未来尚不清楚。因此,似乎两个项目将来可能会合并。
微服务框架全方位大pk!
为了比较上述4个微服务框架,我已经使用它们实现了一个简单的应用程序。该示例应用程序包括一个用于创建,读取,更新和删除对象的rest接口,以及将这些对象存储到表中的接口。我使用openjdk docker映像运行了所有应用程序。如果该框架支持生成本机graalvm映像,我也比较了它们的性能。
我在以下几个方面对比了它们的性能:
- 把上述的示例应用程序开发出来要多久?要实现这些框架,我必须查看框架官方文档以及在诸如stack overflow之类的平台上搜索信息。
- ** 编译应用程序需要多长时间?**我已经测试了执行干净构建所需的时间,包括生成docker映像。对于graalvm,这包括生成本机映像的时间。
- 启动应用程序需要多长时间?在这里,我测试了从运行docker up到应用程序正确响应第一个http请求之间的时间。另外,我还比较了启动后测试的空闲应用程序的内存占用量。
- 应用程序支持请求负载情况如何?我使用jmeter进行负载测试,并对应用程序进行了测试,其中25%的请求执行数据库写入,而75%的请求仅执行数据库读取。然后,我再次根据其峰值性能来测量应用程序的内存占用量。
我在具有四个intel haswell cpu和15 gb内存且运行ubuntu 19.01的google cloud platform虚拟机上执行了所有测试。所有测量均已重复多次,以避免干扰因素。您可以在github上找到使用的脚本以及原始数据。
https://github.com/lizzythelizard/medium-java-framework-compare/tree/master/compare
测试结果
1 上手难度
由于我以前就有spring boot的知识,所以这是一个不公平的比较。但是,在查询文档以及可用的信息和示例时,spring确实是迄今为止使用起来最简单的框架。
micronaut的文档做得很好,并且具有与spring和grail类似的api。因此,spring开发人员很容易开始使用它。
我认为,quarkus的学习曲线较为陡峭,因为与spring和micronaut相比,库和api的成熟度较低。我特别缺少简单的数据库访问权限。
在我看来,helidon显然是最后一名,因为我为应用程序运行付出了很大的努力。
2 编译时间
所有框架,使用openjdk时的编译时间都非常相似,并且在6.98秒(使用jdbc的spring)和10.7秒(quarkus)之间。
但是,原始graalvm映像的生成非常耗时,花费了231.2秒(使用jdbc的micronaut)和351.7秒(使用jpa的micronaut)之间。这使得本机映像对于开发基本上毫无用处,因为等待四分钟来编译一个简单的应用程序实在太多了。
3 启动运行时间
使用spring data的spring boot应用程序平均花了8.16秒来启动。删除jpa和spring data可以将其减少到5.8秒。
正如官方所说,micronaut(使用jpa的时间为5.08秒,使用jdbc的时间为3.8秒)和quarkus(5.7秒)都保证了缩短启动时间的承诺。
helidon mp甚至比spring慢-平均耗时为8.27秒。
但是,真正的赢家是graalvm。本机映像的启动时间在1.39秒(quarkus)和1.46秒(使用jdbc的micronaut)之间,比openjdk实现要快得多。
所有框架运行时使用的内存使用情况非常相似。spring分配了420 mb内存(使用spring data)和261 mb(使用jdbc)。使用jpa时micronaut的内存为262 mb,使用jdbc时为178 mb。197 mb的quarkus表现更好。helidon mp耗时414 mb,与spring boot类似。
同样,仅使用7 mb(quarkus)和27 mb(micronaut使用jpa)的内存,原生graalvm映像的表现大大优于openjdk。
4 峰值负载性能
在负载下,spring boot表现出色,能够处理每秒342(使用spring data)和216(jdbc)请求(r/s),并使用581 mb(spring data)和484 mb(jdbc)内存。helidon显然是最后一名,只能提供175 r/s的速度,同时分配超过1 gb的内存。
其他框架能够在400 r/s(quarkus作为本机映像运行)和197 r/s(openjdk上的quarkus)之间提供服务。各种micronaut实现介于两者之间,与jdbc相比,jpa和本机映像比openjdk略有优势。
在内存使用方面,openjdk上的quarkus表现出色,仅消耗255 mb内存。这甚至比同一个应用程序作为本机映像运行要少得多,该应用程序平均花费368 mb的内存。
但是,micronaut却非常浪费。在openjdk中运行的jpa实现平均使用880 mb,比spring的内存使用量高50%以上。但是,使用jdbc和本机映像有助于micronaut将其内存占用空间减少到367.8 mb。
结论
与spring和microprofile之类的现有框架相比,新的java框架micronaut和quarkus保证了更快的启动时间和更低的内存占用。
他们的确兑现了这一诺言-但只有在闲置或负载很小的情况下才可以。在这里,它们的性能优于spring,特别是将它们与本地graalvm图像结合使用时。但是,在高负载下,它们即使在作为本机映像运行时也无法提供太多优势。
到目前为止,spring在开发上给java开发者最佳体验,而且我认为它也仍然是最适合微服务应用程序的java框架(即使启动时的性能比较差)。
让我感到惊讶的是,使用hibernate / jpa / spring data的成本非常高。即使对于这个非常简单的应用程序,在内存(以及r/s)方面的开销也是巨大的。在这里,我特别喜欢micronaut data的解决方案,该解决方案无需jpa即可自动生成dao代码。我认为micronaut data以后可以添加到spring data方案中。
事实证明,本机graalvm映像在启动时具有令人难以置信的快速性和内存效率,但是在负载下,它们并没有明显的优势。由于本机graalvm的生成会带来一些额外的困难,并且编译时间会急剧增加,因此该技术目前仅在需要快速启动时才有用。例如在serviceless架构中。
欢迎关注我的公众号::一点教程。获得独家整理的学习资源和日常干货推送。
如果您对我的系列教程感兴趣,也可以关注我的网站: