19

云原生的 Java与Golang

 4 years ago
source link: http://cloud.51cto.com/art/202007/620447.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Java曾经著名的座右铭:"一次编写并在任何地方运行"如今已经过时了,我们想要运行代码的唯一地方是在容器内。 "及时"编译器没有任何意义。

由于这个原因,Java生态系统可能正处于其转型之中,以便更好地适应云。 Oracle的GraalVm允许将字节代码编译为Linux可执行文件(ELF)和Rad Heat的Quarkus以及其他框架,以使其像引导一个反应应用程序一样容易。 Quarkus还以Netty和Vertx.x为核心来构建非常有效的响应式Web服务。

31730d294026ee2ebdfaaa8805f07f19.jpeg-wh_651x-s_908707339.jpeg > quarkus official performance stats

Java编译为可执行的二进制文件,可在毫秒内启动,并且占用的内存很小。 这可以利用Java生态系统,甚至可以用其他JVM语言(例如Scala和Kotlin)编写!

听起来好得令人难以置信……

如果您不相信,可以使用在线项目生成器或通过使用maven插件在本地生成项目来玩Quarkus。

另一方面,Golang诞生于云中,当在容器中运行时,没有留下任何负担。 它被认为是云的编程语言。 从第一天开始,小型二进制文件,快速启动程序,较小的内存占用量就可以了。 并且被广泛采用。 对Java世界的严峻挑战。

Java有机会吗? 只有时间证明一切。 但是,出于好奇,我想将Java云原生服务与golang同类服务在性能和开发经验方面进行比较。

在这篇文章中,我将强调两项服务。 比较他们的CPU,RAM,延迟和正常运行时间。 这些服务将在具有相同资源分配的容器中启动,并且Apache基准测试将使他们汗流sweat背。

对于我的案例研究来说,这是一个"足够好"的基准,因为我不认为找到最佳/最差的基准结果,而是比较在相同环境下执行的两个基准。

场景

两种服务都将连接到在另一个容器中运行的MySQL数据库,该容器具有一个表和三行。

imIbmqV.jpg!web > the database

每个服务将获取所有三行,将其转换为域对象,然后编写JSON数组响应。

Apache基准测试将运行10K请求,并发级别为100,这是quarkus JVM版本的两倍(还用于测试"冷" /"热" JVM))

I7zQVvI.jpg!web > the apache benchmark command

Golang服务

使用称为gin的流行的反应式Web框架,该框架具有出色的基准。

在寻找golang非阻塞MySQL驱动程序时,我一无所获,互联网上建议同时使用go-sql-driver,这就是我要使用的。

golang样式非常明确。 一个在你脸上的态度。 主要功能启动服务器,配置请求处理程序,并打开数据库连接。

构建本机go可执行文件

ZZb2EjI.jpg!web > Easy and fast build process. The only tool I had to use was the go compiler. No hustle at all.

Kotlin Cloud本机服务— Quarkus

这是一个Kotlin示例,大致遵循quarkus反应式MySql扩展指南。

7952415656aacef73f766700d7d3a2ff.jpeg > datasource configuration

与go版本相比,存在一些隐式东西,CDI依赖注入,使用javax注释的声明性路由,自动配置解析以及数据源/连接创建/服务器引导程序。 但这是使用框架的代价,它为您带来繁重的工作,并决定了它的工作方式。 但是,它比go版本要短得多,只要我不介意黑魔法就行!

底层有一个Netty反应式Web服务器,由Vert.x多事件循环包装,而Vert.x反应式MySQL驱动程序可以通过一个线程处理多个数据库连接。

另外,我可以使用Kotlin令人惊叹的收藏库来折叠一个列表,其中go版本还没有泛型(但即将推出),也没有丰富的标准收藏库,我不得不手动编写或生成它。

构建Java本机可执行文件

FjyeymA.jpg!web > It took 4 minutes, partly because Gradle executes the native image compilation inside a Linux Graa

基本上,我能够弄清楚构建本机可执行文件的容器中发生的事情是SubstrateVM。 设计为可提前编译的可嵌入虚拟机链接到我们的代码,并作为一个单元进行编译。 甲骨文表示,这是惊人的,但并非没有代价,SubstrateVM的优化次数少于HotSpot Vm,并且垃圾回收器更简单。

执行此操作的编译器称为" Graal",它与语言无关,在使用Java字节码之前,需要先将其翻译为中间表示形式,即Truffle语言。 这非常有趣,可以在这篇文章中找到有关Graal和Truffle的详尽解释。

构建Java本机图像看起来更加复杂,速度较慢,并且生成的二进制文件几乎是文件的两倍。 但这有效! 与一个Java Uber(胖)Jar相比,35M可执行二进制文件实际上是什么,它可以轻松地大十倍。 35MB甚至可以放在aws lambda中。

强调服务

我正在使用以下设置在本地计算机上运行所有测试:

zYRnIb3.jpg!web

不适使用:

  • MacBook Pro(15英寸,2017年)
  • 2.9 GHz Intel Core i7(8核)
  • 16 GB 2133 MHz LPDDR3

不适使用名为cAdvisor的工具来监视我的容器的状态。

场景

  • quarkus jvm热点容器
  • quarkus java本机容器
  • golang容器

每个都分配了以下资源

  • 100MB / 0.5 CPU | 200MB / 1个CPU | 300MB / 2个CPU

我对……感兴趣

  • cpu / ram利用率(多核的利用率)
  • cpu / ram峰值
  • cpu / ram空闲
  • 引导时间
  • 响应潜伏时间平均值/最大值
  • 吞吐量(每秒请求数)

现在,我将运行许多基准测试,并为每个基准收集许多数据点。 如果有太多信息,请随时跳至摘要结尾

github repo以及该实验的所有代码都可以在这里找到

quarkus jvm热点— 100MB / 0.5 CPU

  • 闲置CPU使用率0.25%
  • 空闲ram使用情况66MB
  • 自举时间6s
uqQrYfA.jpg!web > CPU usage during bootstrap. ( a spike , probably jit + launching JVM )

第一轮压力测试(Cold JVM)

令人惊讶的是,没有失败的请求。

iUnANvi.jpg!web > CPU usage during stress.
QjayEfE.jpg!web > RAM launched from 60 to almost 100 MB (limit) and stayed there.

第2轮压力测试(温暖的JVM)

quarkus jvm热点— 200MB / 1个CPU

  • 闲置CPU使用率0.13%
  • 空闲ram使用情况66MB
  • 引导时间3s
mEfIJzR.jpg!web > CPU usage during bootstrap. ( a spike again )

第一轮压力测试(Cold JVM)

f424e1d3eaca2824feb8f9f0170fe33b.jpeg > CPU / RAM usage under stress
rEzuyue.jpg!web > Surprisingly the JVM did not eat all the allocated 200MB and 140MB was sufficient

第2轮压力测试(温暖的JVM)

quarkus jvm热点— 300MB / 2 CPU

  • 空闲cpu / ram与以前的方案相同
  • 引导时间1.1s(NICE)
qUf63yv.jpg!web > CPU usage during bootstrap, a spike again.

第一轮压力测试(Cold JVM)

N7fM7bR.jpg!web > Good CPU utilzation
jqUFfif.jpg!web > 142 mb ram was sufficient

第2轮压力测试(温暖的JVM)

现在,让我们看看本地图像将如何执行。

quarkus Java Native — 100MB / 0.5 CPU

  • 引导时间:0.125s。 (!!!)
  • 启动时没有CPU高峰
Efimua2.jpg!web > cpu / ram during bootstrap

压力测试结果

qmANNje.jpg!web > CPU reached 0.5 limit as expected
Qjauae6.jpg!web > Good ram usage, 19MB active memory. WOW

quarkus Java Native — 200MB / 1个CPU

  • 即时引导(0.0125s)
  • 4空闲ram用法
  • 在压力下使用19种内存
  • 100%的CPU使用率
  • 启动时没有CPU高峰

检测结果

quarkus Java Native — 300MB / 2 CPU

没提升。

golang — 100MB / 0.5 CPU

  • 空闲CPU 0
  • 闲置内存2.3MB(不错)
  • 引导时间:几分之一秒
  • 启动时没有CPU高峰

结果有点歪斜。 由于某种原因,一小部分请求需要大约7秒钟才能完成。

当再次尝试运行测试以查看偏斜结果是否能够再现测试时,实际上是否已将其压碎!

运行时错误:无效的内存地址或nil指针取消引用。 嗯…可能是我做错了什么? 似乎go-sql库中存在错误。 如文档所述,从表中读取的代码是100%,并且99%的时间都可以工作。 这不应该发生。

golang — 200MB / 1个CPU

我不断收到运行时错误。 可疑总是在测试结束时。 但是,go-mysql驱动程序的校正不是主要问题,因此在完成90%的请求后手动终止测试。

  • 压力下的CPU / RAM使用率
2uERJnY.jpg!web > cpu utilization during stress
c57e5cd59de3955916c890dedd082bb6.jpeg > RAM usage during stress. 12.27MB, very nice.

golang — 300MB / 2个CPU

没有明显的改善,所有统计数据几乎相同。 CPU利用率低于1.0。 我不知道为什么go不能充分利用更多的内核,有趣的是……可能是因为该过程受IO约束,或者可能是杜松子酒需要手动配置才能更好地利用多个内核。

摘要

55e8101e0cf6f2743851a942312948ae.jpeg > aggregated stats ( warm jvm/native image | golang )

似乎Quarkus已准备好投入生产,它允许简单的JVM /本机发行版/开发模式,并允许在本地运行本机测试。 而且,只要您不使用反射或JNI,就可以安全地配置GraalVM。 否则,您将必须自己配置graal编译器,并且也有针对此的现有解决方案。

延迟和吞吐量

golang和云原生Java均产生了相似的结果,尽管平均而言稍微偏爱golang服务。 但是,java本机结果更加稳定。 Golang服务有时会在1.25µs内做出响应,而很少在7s内做出响应。

"预热"后的JVM产生了良好的结果,但比本机或go版本差。

CPU利用率

当给定的内核少于单核时,go和native-java在负载下均表现不佳,而在使用2个内核启动时,它们并没有表现出明显的改进。 可能是因为工作负载受IO限制。 或者因为gin / Netty的默认配置没有考虑多个内核。

另一方面,JVM利用了赋予它的所有内核,并在各个方面提高了性能。

RAM使用

压力很大,java本机为40MB,golang服务为24MB。 两种情况都不错,尽管golang版本使用的ram几乎少了两倍。

JVM在压力下使用了140MB。 完全是官方的quarkus统计信息。 对于JVM来说一点都不差,但是几乎是golang版本的6倍。

引导时间

golang和云原生Java均会立即启动,而JVM版本则需要几秒钟(取决于分配的CPU),并在启动时产生CPU峰值。

开发经验

这更是一个宗教问题,而不是一个实际问题。如此病态,请谨慎回答。 Quarkus创建Java世界中非常熟悉的抽象(例如基于注释的DI)。它为您启动服务并创建连接池。可以使用丰富的收藏标准库和泛型。但是,这种感觉有点像黑魔法,一旦停止工作,您会感到无助。此外,将Java代码编译为本地二进制文件并不是那么简单,您必须意识到其中的局限性和注意事项,尽管Red Hat在扩展方面取得了很大的进步,但并非每个Java库都将与本地编译兼容。 。 (预先配置为本地编译的Java库)。使用与本机编译不兼容的库(例如Guice)将需要您手动配置Graal VM。这是可能的,但并非像使用广口瓶那样直接。 Quarkus和Graal VM也"相对"新。因此,有许多冒险等待着。但由于是双模式(JVM或本机)。万一本机版本停止工作,总会有一个退路,这是解决任何新出现问题的好方法。

另一方面,Golang仅在现在(存在10年后)才承认需要泛型。 当然,它不喜欢隐性事件的继续。 从很多方面来说,这都是好事。 另外,尽管go社区在追赶方面确实做得很好,但是可用的工具和库却更少(例如,只有一个流行的阻塞MySQL驱动程序)。 另一方面,它的编译和构建过程非常快速/简单。 每个golang软件包都将为您工作,而不受Java本地平台引入的限制。

结论

Java成为云原生,Golang并没有像JVM那样过度地执行它,这是非常好的。 我相信它将来会被广泛使用。 但是golang绝对可以打架。

因此,请谨慎选择!

而且不要忘了给仙人掌浇水


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK