Java曾经的名言:“Writeonce,runeverywhere”已经很过时了,因为现在我们只想在容器中运行代码。在容器中,“及时”编译器意义不大。出于这个原因,并且可能为了更好地适应云计算,Java生态系统正在转型。Oracle的GraalVm允许将字节码编译成Linux可执行文件(ELF),而RadHeat的Quarkus和其他框架旨在使响应式服务更容易。Quarkus以Netty和Vertx.x为核心,可以用来构建非常高效的响应式Web服务。Java编译成可执行的二进制文件,启动时间为毫秒,内存占用量小。这使得利用Java生态系统甚至使用Scala和Kotlin等其他JVM语言编写成为可能。您可以使用在线项目生成器来使用Quarkus,或者使用maven插件在本地生成项目。而Golang则是为云而生,在容器中运行时,没有遗留负担。它被认为是云的编程语言。生成的二进制可执行文件体积小,启动速度快,内存占用小,这是Go从一开始就具有的特性。Golang的流行对Java世界提出了严峻的挑战。Java有没有机会,也许只有时间才能告诉我们最终的答案。不过,出于好奇,我想从性能和开发体验上比较Java和Golang云原生服务。在本文中,我将用两种语言编写相同的服务。比较它们的CPU使用率、RAM、延迟和运行速度。这些服务会在相同资源分配的容器中启动,使用ab进行测试。对于我的案例来说,这是一个“足够好”的基准测试,因为我不假设找到最好/最差的基准测试结果,而是在同一环境中运行两个基准测试以进行比较。场景这两个服务将连接到另一个容器中运行的MySQL数据库,其中包含一个表和三行数据。每个服务获取所有记录,将它们转换为对象,并输出一个JSON数组。ab将发出10K请求,并发级别为100,并且quarkusJVM版本运行两次(用于测试“冷”/“暖”JVM)。Go语言版本Go语言版本使用gin框架。#theservicepackagemainimport("database/sql""fmt""github.com/gin-gonic/gin"_"github.com/go-sql-driver/mysql""net/http")typeFruitstruct{Idint`json:"id"`Namestring`json:"name"`}varcon*sql.DBfuncinit(){//openingamysqlconnectionpoolwithanothercontainerdb,err:=sql.Open("mysql","root:password@tcp(host.docker.internal:3306)/payments")iferr!=nil{panic("failedtoopenamysqlconnection")}con=db}funcmain(){r:=gin.Default()r.GET("/fruits",fruits)r.Run()//serverupon8080}//THEREQUESTHANDLERfuncfruits(c*gin.Context){fruits:=getFruits()c.JSON(http.StatusOK,fruits)}funcgetFruits()[]Fruit{rows,_:=con.Query("SELECT*FROMfruits")fruits:=[]Fruit{}forrows.Next(){varrFruitrows.Scan(&r.Id,&r.Name)fruits=append(fruits,r)}returnfruits}Golang的MySQL驱动使用go-sql-driver。golang的代码风格非常清晰。一切尽在眼前的态度。主要功能启动服务器,配置请求处理程序,并打开数据库连接。编译本机可执行Kotlin版本包org.acmeimportio.vertx.core.json.JsonArrayimportio.vertx.core.json.JsonObjectimportio.vertx.mutiny.mysqlclient.MySQLPoolimportio.vertx.mutiny.sqlclient.Rowimportio.vertx.mutiny.sqlclient.RowSetimportjava。util.concurrent.CompletionStageimportjavax.inject.Injectimportjavax.ws.rs.GETimportjavax.ws.rs.Pathimportjavax.ws.rs.Producesimportjavax.ws.rs.core.MediaType@Path("/fruits")classFruitResource{@field:Injectlateinitvarclient:MySQLPool@GET@Produces(MediaType.APPLICATION_JSON)funlistFruits():CompletionStage{returnclient.query("SELECT*FROMfruits").execute().map{rows:RowSet->rows.fold(JsonArray()){array,row->array.add(JsonObject().put("id",row.getLong("id")).put("name",row.getString("name")))}}.subscribeAsCompletionStage()}}使用QuarkusReactMysql扩展的数据库连接。与Go版本相比,代码有很大不同,例如CDI依赖注入、使用javax注解的声明式路由、自动配置解析以及数据源/连接创建/服务器引导。这就是使用框架的代价,它会为您完成繁重的工作,并规定应该如何完成工作。但是,它比go版本的代码要短得多。这里使用Netty响应式Web服务器,由Vert.x多事件循环和一个Vert.x响应式MySQL驱动程序包裹,它可以用一个线程处理多个数据库连接。另外,我可以使用Kotlin的集合库的折叠功能,它还没有通用的Go版本。编译可执行文件的Java版本我已经弄清楚构建过程中发生了什么,其核心是SubstrateVM。它被设计为AOT过程中的可嵌入虚拟机,它链接到我们的代码并作为一个单元进行编译。不过,据Oracle称,SubstrateVM的优化程度低于HotSpotVm,垃圾收集器也更简单。AOT编译器称为“Graal”,它与语言无关。Java字节码需要被翻译成中间表示(Truffle语言)。这可以在与Graal和Truffle相关的文章[1]中找到。构建Java原生可执行文件看起来更复杂,编译速度更慢,生成的二进制文件的大小几乎是Go版本的两倍。但是,一个35M的可执行二进制文件仍然比JavaFatJar小很多。35MB甚至可以让您运行awslambda。对于压力测试,我是在本地运行所有的测试,设置如下。MacBookPro(15-inch,20172.9GHzIntelCorei7(8cores).16GB2133MHzLPDDR3使用cAdvisor的工具监控容器状态。场景QuarkusJVMhotspotQuarkusJavanativeGolang以上每个场景都在100MB/0.5CPU|200MB/1CPU|300MB/2CPU在以下三个配置上进行了测试。我主要关心的是:cpu/ramutilization(multi-coreutilization)cpu/rampeakcpu/ramidlestartuptimeresponsedelayavg/maxThroughput(requestspersecond)testresultslookslikeQuarkusisproductionready,itallowseasyJVM/nativerelease/devmode,andallowsrunningnativetestslocally.只要你不使用反射或JNI,根据配置是可行的GraalVM的。否则,您将不得不自己配置graal编译器,但是现在有解决方案。延迟和吞吐量Golang和原生Java测试结果比较接近,尽管平均而言Golang版本测试结果略好一些。不过JavaNative版本的测试结果更加稳定。有时Golang服务在1.25μs内完成响应,有的需要7s才能完成。“预热”JVM版本的结果还不错,但比Native或Go版本稍差。当CPU利用率使用0.5核时,Go和native-java似乎在负载下表现不佳,并且从2核开始时没有明显改善。这可能是因为工作负载的瓶颈是IO。或者是因为gin/Netty的默认配置没有考虑到多核问题。而JVM版本利用所有给定的内核并在各个方面提高性能。内存使用有压力,Javanative使用40MB,Golang使用24MB。在这两种情况下都不错,尽管Golang版本使用了几乎两倍的内存。JVM使用了140MB。与Quarkus官方统计完全一致。对于JVM来说还不错,但比Golang版本高出近6倍。启动时间Golang和云原生java都立即启动,但是JVM版本需要几秒钟(取决于分配的CPU)并在启动时产生CPU峰值。如果配置不当,会导致k8sHPA疯狂增加pod。发展经验这与其说是一个实际问题,不如说是一个宗教问题。Quarkus使用Java世界中常见的抽象(例如基于注解的DI)。它启动服务并为您创建连接池。它可以使用一组丰富的标准库和泛型。然而,这感觉有点像黑魔法,一旦某些组件不起作用,您就会感到无助。此外,将Java代码编译成本机二进制文件并不是那么简单,您必须了解一些限制和注意事项,并非每个Java库都与本机编译兼容。一旦使用不兼容的库(例如Guice),您就需要自己配置GraalVM。Quarkus和GraalVM是“相对”新的。所以可能会有一些问题。但是由于双模式(JVM或本机)。万一本机版本的某些组件停止工作,总是有一个回退,这是解决任何新问题的一个很好的解决方法。另一方面,Golang在成立10年后才承认它需要泛型。而且它肯定不喜欢框架使用了很多魔法。这在很多方面既是好事也是坏事。此外,虽然Go社区做得很好,但可用的工具和库相对较少。然而,它的编译和构建过程更快/更简单。并且兼容各个Golang包,不受java-native平台带来的限制。结论Java已经为云原生做好了准备,Golang也不远了。相信CloudNativeJava在未来会被大规模使用。原文地址:https://medium.com/swlh/cloud-native-java-vs-golang-2a72c0531b05本文转载自微信公众号《高可用架构》,可通过以下二维码关注。如需转载本文,请联系IgorDomrev公众号。