当前位置: 首页 > 科技观察

Java微服务vsGo微服务,谁更强?

时间:2023-03-21 18:32:40 科技观察

Java微服务能和Go微服务一样快吗?这是我最近一直在思考的一个问题。在去年8月的OracleGroundbreakersTour2020LATAM大会上,MarkNelson和PeterNagy进行了一系列的基础测试进行对比。接下来我就给大家介绍一下。在程序员圈子里,普遍的看法是Java老、慢、无聊,而Go又快、新、酷。为了尽可能进行相对公平的测试,他们使用了一个非常简单的微服务,没有外部依赖(例如数据库),非常短的代码路径(只是操作字符串),使用小型轻量级框架(HelidonforJava和GotoolkitforGo),尝试了不同版本的Java和不同的jvms。再来看看擂台两侧的选手:身着深色制服的选手是JAVAJava是由SunMicrosystems开发的,后来被甲骨文收购。它的1.0版本发布于1996年,最新版本是2020年的Java15。主要设计目标是Java虚拟机和字节码的可移植性,以及带垃圾回收的内存管理。它是世界上最流行的语言之一,在开源环境中开发。先来看JAVA的问题。一般认为它最大的问题是速度慢,慢到不再合理,反而更有历史意义。但多年来,Java产生了许多不同的垃圾收集算法来加速其运行。Oracle实验室最近开发了一个新的Java虚拟机GraalVM,它有一个新的编译器和一些令人兴奋的新特性,例如能够将Java字节码转换成无需javavm操作就可以使用的原生图像等。而它的对手是年轻而充满活力的GOGO由谷歌的罗伯特·格里默、罗伯·帕克和肯·汤姆森共同创立。他们对UNIX、B、C、Plan9、UNIX窗口系统等做出了重大贡献。GO是开源的,2012年发布了1.0版(比JAVA晚了16年),2020年发布了1.15版。无论是采用率还是语言和工具生态系统本身,它都在快速增长。GO受到C、Python、JavaScript和C++等多种语言的影响。旨在成为高性能网络和多处理的最佳语言。StackOverflow有27,872个“Go”问题,而Java只有1,702,730个。可见长江后浪推前浪。Go是一种静态类型的编译语言。它有称为goroutines的轻量级进程(这些不是OS线程),它们之间具有独特的通信通道(类型化,FIFO)。Go是许多CNCF项目(例如Kubernetes、Istio、Prometheus和Grafana)的首选语言。赛前对比从我个人的角度来看,与JAVA相比,Go有以下优势:Go更容易实现复合、纯函数、不变状态等功能模式。Go处于其生命周期的早期,因此它没有向后兼容性的沉重负担——Go仍然可以轻松地打破某些限制进行改进。Go编译为本机静态链接二进制文件——没有虚拟机层——二进制文件具有运行程序所需的一切,这对于“从头开始”的容器来说非常有用。Go体积小,启动速度快,执行速度快(目前是)。Go没有OOP、继承、泛型、断言、指针算法。Go少了括号。Go没有循环依赖,没有未使用的变量或导入,没有隐式类型强制转换。Go的样板代码少得多。缺点是:Go工具生态系统不成熟,尤其是依赖管理——有几种选择,没有一种是完美的,特别是对于非开源开发;仍然存在兼容性挑战。使用新的/更新的依赖项构建代码非常慢(就像Maven著名的“下载互联网”问题)。将绑定代码导入存储库,使在存储库中移动代码成为一场噩梦。调试、分析等仍然是一个挑战。使用指针。需要实现一些基本算法。没有动态链接。没有太多旋钮可以调整执行或垃圾收集、配置文件执行或优化算法。比赛开始使用JMeter来运行负载测试。测试多次调用服务并收集有关响应时间、吞吐量(每秒事务数)和内存使用情况的数据。对于Go,收集常驻集大小;对于Java,跟踪本机内存。该应用程序在测量前通过1000次服务调用进行了预热。应用程序本身的源代码以及负载测试的定义位于此GitHub存储库中:https://github.com/markxnelson/go-java-go第1轮在第一轮测试中,在“小型”测试的机器是一台2.5GHz双核英特尔酷睿i7笔记本电脑,配备16GB内存,运行macOS。测试运行了100个线程,每个线程有10000个循环,上升时间为10秒。Java应用程序运行在JDK11和Helidon2.0.1上。使用Go1.13.3编译的Go应用程序。结果如下:可见,围棋赢了第一局!JAVA占用内存太大;预热对JVM影响很大——我们知道JVM会在运行时进行优化,所以这是有道理的。在第一轮的基础上,引入了GraalVM镜像,让Java应用的执行环境更接近Go应用的环境,增加了GraalVM镜像测试(使用GraalVMEE20.1.1-搭建的本机)JDK11图像)结果:通过使用GraalVM图像在JVM上运行应用程序,我们没有看到吞吐量或响应时间有任何实质性改进,但内存占用确实变小了。下面是一些测试的响应时间图:第2轮在第二轮测试中,使用了一台更大的机器来运行测试。36核(每个核两个线程),256GB内存,运行oraclelinux7.8的机器。与第一轮类似,使用100个线程,每个线程10,000个循环,10秒的启动时间,以及相同版本的Go、Java、Helidon和GraalVM。结果如下:这一轮GraalVM图像获胜!以下是一些测试的响应时间图:Java变体在此测试中表现更好,并且在不使用Java日志记录的情况下大大优于Go。Java似乎能够更好地使用硬件提供的多核和执行线程(与Go相比)。这一轮最好的成绩来自于GraalVM原生镜像,平均响应时间为0.25毫秒,每秒处理82426个事务,而Go的最好成绩是1.59毫秒,39227tps,但这在内存消耗!GraalVM镜像比在jvm上运行的相同应用程序快大约30-40%!第3轮这一次,比赛在Kubernetes集群中运行这些应用程序,这是一个更自然的微服务运行时环境。这次我使用了一个Kubernetes1.16.8集群,有三个工作节点,每个节点有两个核心(每个核心有两个执行线程),14GB内存和oraclelinux7.8。应用程序访问是通过Traefik入口控制器进行的,JMeter在Kubernetes集群外部运行以进行某些测试,而对于其他测试则使用ClusterIP并且JMeter在集群内部运行。与之前的测试一样,我们使用了100个线程,每个线程使用了10,000个循环,并且启动时间为10秒。以下是各种容器的大小:Go11.6MB11.6MBJava/Helidon1.41GB1.41GBJava/HelidonJLinked150MB150mbNativeimage25.2MB25.2MB结果如下:以下是一些测试的响应时间图:在这一轮,我们观察到Go有时更快,而GraalVM图像有时更快,但两者之间的差异很小(通常小于5%)。Java似乎比Go更擅长使用所有可用的内核/线程——我们在Java测试中看到了更好的CPU利用率。Java在具有更多内核和内存的机器上性能更好,Go性能在更小/功能更弱的机器上更好。在“生产规模”的机器上,Java很容易与Go一样快,甚至更快。