【AICon】探索RAG 技术在实际应用中遇到的挑战及应对策略!AICon精华内容已上线73%>>> 了解详情
写点什么

为 Java 提速:用 Quarkus 开发 Kubernetes Native Java 应用

作者:Jason Greene, John Clingan, Eric Deandrea

  • 2022-05-02
  • 本文字数:7566 字

    阅读完需:约 25 分钟

为 Java 提速:用 Quarkus 开发 Kubernetes Native Java 应用

Java 在企业应用中占据主导地位。但在云上,Java 比一些竞争对手的使用成本更高。原生编译降低了在云中使用 Java 的成本:它创建的应用程序启动速度更快,占用的内存更少。

 

原生编译给 Java 用户带来了许多问题:原生 Java 对开发有什么影响?我们什么时候应该改用原生 Java?什么时候不应该?我们应该使用什么框架来开发原生 Java?这个系列将回答这些问题。

Kubernetes Native Java 是什么?


“[它们]可以运行 3 倍密度的部署,而又不影响服务的可用性和响应时间。”—— Lufthansa Technik


采用 Kubernetes,组织可以更快、更经济有效地交付业务价值。为什么?因为 Kubernetes 简化了一些原本困难的任务:它会在负载增加时自动实现应用程序的水平扩展,并能执行滚动升级。

 

但是,这种方法与 Java 传统的三层架构模型(大堆、垂直扩展、长时间运行的进程)相冲突。

 

历史上,Java 通过分配和最大化持有的系统资源,来适应随时间变化的负载,实现卓越的性能。

 

开发人员非常喜欢长时间运行的、重量级的、JVM 高度动态的 Java 模型,以便在运行多个不同的应用程序时可以支持丰富的声明式编程模型。延迟扫描和运行时类路径分析给我们带来了控制反转,减少了样板代码——以启动时间和内存占用为代价。这些缺点在堆很大的大型单体应用程序中并没有太大影响,因为启动代价只需要支付一次。

 

但是,在 Kubernetes 中运行这些传统的 Java 应用程序会导致许多胖 JVM,它们承载着需要大量资源的动态运行时,并且会经常重启。重启要一次次分析同样的不可变镜像,每次都会浪费掉相同的 CPU 周期。这个分析还会使用大量的内存,而且在 JVM 生命周期中一直占用。但是在 Kubernetes 中,部署密度很重要:可以运行的应用程序实例越多,Kubernetes 的投资回报率就越高。

 

Kubernetes Native Java 意味着 Java 应用程序可以利用而不是抗拒 Kubernetes 的能力——启动速度很快的小进程,而不是启动时间很长、内存占用很高的 JVM。

Quarkus 背后的动机


“在我们推出 Quarkus 之前,许多客户已经开始关注 Go 和 Node.js 等技术栈,希望可以借此提高性能和效率。对于选择一种新的语言以及雇用新的开发人员,或者重新培训现有的 Java 开发人员,这些客户都倍感疲倦。” —— Arijit Mazumdar,WiPro


组织以及开发人员都在 Java 上有很大的投资。它是一种令人难以置信的高产语言,有许多开发人员熟悉并喜爱的神奇工具和框架。然而,Kubernetes 需要一个新的方向。虽然放弃 Java 的许多好处令人遗憾,但能更有效利用内存、缩短启动时间的运行时,如 Node.js 和 Golang,对 Java 投资发起了挑战。

 

Quarkus 团队则希望两者兼得:将功能丰富、成熟的 Java 生态系统的优势与 Kubernetes 的操作优势相结合。要实现这一点,Java 应用程序运行时必须更多地采用静态定义,但仍然包含让开发人员可以更快迭代的 Java 动态特性。

 

组织使用云增加了应用程序部署的速度。同样,现如今的业务需要缩短开发周期,加速业务价值交付。Quarkus 所采用的方法特别注重“开发乐趣(Developer Joy)”,可以支持迭代和部署速度的提升,并且已经证明 Java 可以比脚本语言更具生产力。

 

当然,生产力并不仅仅与编码速度有关。构建现代云应用程序需要与其他服务交互,而使用复杂 YAML 的配套技术会降低你的速度。我们认为,关乎“开发乐趣”的特性,比如开发服务和零配置开发(稍后会详细介绍),对于 Kubernetes Native Java 来说必不可少。例如,你编写访问数据库的代码,Quarkus 会在你输入的时候神奇地启动并连接后台的所有一切。

 

同样,向 Kubernetes 部署时也不需要阅读 Kubernetes 管理方面的书籍以及编写数百行的 YAML 文件。我们认为,对于 Quarkus,很重要的一点是你只需要了解 Java。在构建时添加“-Dquarkus.kubernetes.deploy”标志,Quarkus 就会将应用程序部署到 Kubernetes 上(不要忘记登录!)如果你愿意,可以使用集成的 Quarkus Dev UI,直接从浏览器触发部署。

 

简而言之,Quarkus 是从头开始构建的,它将 Java 变成了构建本地二进制文件和 Kubernetes 应用程序的理想语言。Quarkus 是 Kubernetes Native Java!

“构建时”促进原生编译


“Quarkus 引入了一种新的范式,它颠覆了底层的运行方式——支持原生模式,所有反射都是在编译时完成,而不是运行时完成,这太神奇了!” —— Roberto Cortez,Talkdesk(目前受雇于红帽公司)


按照 Kubernetes 原生架构的要求,将应用程序的生命周期划分为两个不同的阶段,是实现可瞬间启动的小型轻量级进程的关键。传统上,Java 应用程序运行时启动要执行一系列复杂的、长时间运行的、动态的自省步骤,以满足动态部署环境的要求。这些步骤在应用程序每次启动时都要重复进行。



由于容器镜像不会变化,所以不需要在运行时再执行这些步骤。大多数动态启动步骤都可以移到构建时。在实际开始时执行的工作将少很多,速度会明显加快。此外,还可以丢掉应用程序不需要的代码。通过对输出进行裁剪可以得到只包含必须代码的更精简的可执行文件。



当企业在评估和准备 Java 原生可执行程序时,Quarkus 直接带来了 JVM 效率方面的优势,因为构建时优势普遍适用:从这种方法中获益的不仅仅是原生编译,还有传统的 JVM/Hotspot。当代码较少、效率较高时,无论其形式如何,输出都会更精简、更快速。也就是说,本地提前编译将这种优势提升到了一个新的水平:在一个封闭的世界里,所有东西都是提前知道的,编译器可以进行非常精细的优化,一直到字段、变量和指令集级。

 

不过,有一个问题。为了使预(AOT)编译器有效地完成工作,它必须能够理解 Java 代码最终会做什么。Java 的动态性使其具有了很高的生产力,同时也使编译器无法掌握应用程序的行为,并限制了优化程度。例如,注入点使代码变得简单,易于演进。但对 AOT 编译器来说,它是一个未定义的、待明确的东西。

 

例如,你可以通过反射将每个类标记为可用,从而轻松得到一个原生可执行文件。但结果可能会让人失望:内存使用量和启动时间只比 JVM 上好一点点。换句话说,你需要一个全面的构建时模型来释放原生编译的所有优势。就像 Quarkus 所做的那样!

 

现在,Quarkus 在构建时解析了每个依赖关系,并生成了一个完整而封闭的应用程序。前面提到的注入示例的问题,从 AOT 编译器的角度来说,已经完全解决了——它现在知道要使用什么类,其余的代码可以去掉了。

 

总之,将 GraalVM 的优化能力与 Quarkus 的构建时能力相结合,可以获得最小的内存占用、最短的启动时间。限制这种开销对于释放 Kubernetes Native Java 的全部优势至关重要。

一流的本地编译


“现在,我们是在容器上以 JVM 模式使用 Quarkus,但将来,我们已经计划在 Kubernetes 和无服务器环境中以原生模式使用 Quarkus。在这些环境中,原生模式将是理想的选择。” —— Edouard Lamotte,Sedona


由于 Quarkus 及其扩展(Quarkus 优化过的库)包含了构建时优化,所以你可以将任何应用程序构建为一个内存占用较小的本地可执行文件。Quarkus 负责所有的工作!其他解决方案通常需要更多的开发人员参与:也许你可以编译成本地程序,但前提是你的应用程序使用了明确支持的 API 子集,或者你用了 GraalVM 提示(hints)注解代码。即使你遵循 GraalVM 的规则,也经常需要保证一个单独的 JSON 配置文件得到及时更新。此外,Quarkus 使开发者能够轻松编写针对本地可执行文件的测试,确保你构建的东西(本地可执行文件)按预期运行。

 

我们认为,平台对原生编译的广泛支持对于有效使用该技术至关重要。出于这个原因,我们不遗余力地简化原生编译的采用。此外,我们确保 Quarkus 中的每个扩展都支持它。我们强烈建议所有“Quarkiverse”扩展(社区贡献的扩展库)也支持原生编译。并非所有的 Java 库都与 GraalVM 的原生编译兼容。然而,由于 Quarkus 有大量的扩展(将近 500 个,而且还在不断增加!),所以很可能你能想到的所有用例都已经覆盖到了。如果没有,为你的用例编写一个 Quarkus 扩展也并不困难。

开发乐趣


“在提升开发体验方面,Quarkus 一直有一些相当惊人的创新。他们的开发模式控制台和 Testcontainer 支持将其提升到了下一个等级。” —— James Ward,Java Champion


如前所述,Kubernetes Native Java 并不仅仅是为了精简可运行文件。它对于提高 Java 的生产力,扩大其相对于其他原生编译语言(如 Golang)的生产力优势,同样至关重要。每一种工具、框架和运行时都声称它能提高开发者的生产力。但 Quarkus 通过提供“开发乐趣”将其提升到了一个新的水平。这是什么意思呢?

 

Quarkus 致力于提供一种有趣的、令人愉快的开发体验,保证 Java 开发者持续参与。Quarkus 的目标是让每个开发者都专注于活跃的编码,因为我们知道,停下来,运行一堆工具,等事情完成,会产生很大的反作用。Quarkus 让开发者非常高效,以至于他们会愿意去做各种尝试——以前,由于截止日期的限制,这可能是不被允许的。以下是几个可以带来“开发乐趣”的特性:


  • 实时编码:所有代码的变更都是实时的,即使是修改依赖关系或配置文件,也不用放弃强类型。每次访问应用程序时,Quarkus 会评估代码的变化,重新生成必要的字节码,并在后台重新加载应用程序以返回更新后的结果,通常只需半秒或更短的时间。实时编码适用于几乎所有的代码修改和重构,无论多么复杂,都不需要 IDE 插件或特殊工具。

  • 开发服务:Quarkus 使用Testcontainers在开发和测试期间自动实例化和配置服务,如数据库、缓存和 Kafka。只需添加一个扩展,然后开始编码即可,容器会在后台自动下载和启动!

  • 零配置开发:约定优于配置原则,意味着开发服务不需要配置。Kubernetes 部署 YAML 是针对特定目标自动生成的,无论是 Minishift、OpenShift,还是普通的 Kubernetes。

  • 持续测试:每次保存文件时自动运行测试。运行所有的测试,失败的测试,或者只运行与变化代码相关的测试(Quarkus 会计算出来)。下图展示了一个实时编码的结果及其持续测试的输出,这使 Java 开发像脚本语言一样高效!



  • Dev UI:在开发过程中,在 Web 浏览器中可视化并利用 Quarkus 扩展功能。下图展示了一个带有 Dev UI 组件的示例应用程序,它允许开发者实时更新配置,查看 CDI Bean,部署到 OpenShift,打开 Swagger UI 等等。



  • 命令模式:在启动 Quarkus 开发模式的终端中,通过键盘提供类似 Dev UI 的功能来补充 Dev UI,比如在不重新启动 JVM 的情况下通过一次按键来改变日志级别。

  • Quarkus CLI:一个用于管理 Quarkus 项目的命令行工具。CLI 可以生成和构建项目,管理依赖关系,运行测试,等等。

  • 标准和优秀框架支持:Quarkus 支持像 MicroProfile 这样的标准,JAX-RS、JPA、JTA 等 Jakarta EE 规范,以及 Apache Camel、Hibernate、Kafka、Spring Compatibility API 等几十个框架和工具。

  • Kotlin 支持:除了 Java 外,Quarkus 还支持另外一种流行的 JVM 语言:Kotlin。

  • IDE 集成:Quarkus 插件可用于 IntelliJ、Visual Studio Code 和 Eclipse 等 IDE,具有增强型代码补全等功能。不过,Quarkus 不需要一个 IDE。开发乐趣特性适用于任何编辑器,甚至是 vi 和记事本!

当这些功能结合在一起时,就可以加快开发团队的速度,使他们能够专注于他们最擅长的工作。这就加速了项目的交付,更快地满足客户需求,最终通过把更多的时间花在业务逻辑上而不是管道上来提高应用程序的质量。

命令式 vs. 反应式

“[Quarkus]天然可以处理反应式或命令式编程。” —— LogicDrop



运行时性能很重要。由于整体资源使用率较低,所以反应式编程模型的扩展效率更高,响应性也更强。但是,实现运行时性能最大化不应该要求重写,也不应该以损失易用性和开发灵活性为代价。因此,Quarkus 建立在以 Eclipse Vert.x 工具包为基础的统一的阻塞/反应式 I/O 栈上。

 

Quarkus 实现了智能路由,使命令式和反应式 API 的结合成为一种常规的开发体验。

 

当一个应用程序使用 Quarkus 的反应式 API 时,代码在 I/O 线程上运行。这就减少了线程上下文切换,并在最大程度上提高了吞吐量,同时最大程度地减少了资源消耗。



当使用命令式 API 开发时,Quarkus 将工作分派给工作线程,完成后再将工作切换回 I/O 线程。

 

即使在同一个应用程序(或同一个类)的开发中,也可以使用阻塞的命令式 API 或异步的反应式 API。借助 Quarkus 智能路由,Quarkus 天生就为这两种方法并存提供了完美的支持。开发人员不需要在编写代码之前就做出选择。

再见了,样本代码!


“[我看到]开发生产力提升了 30%到 40%” —— Christos Sotiriou,Vodafone Greece


Quarkus 社区坚信,你在 Quarkus 项目中输入的所有内容都应该是超级简洁且容易理解的,并且不需要过多的使用光标键!Quarkus 包括了许多流行的 API,并对它们做了增强,以便它们可以利用 Quarkus 的构建时信息。

 

例如,Quarkus 用“Panache”增强了 Hibernate,这是一组 API 扩展,使数据访问像现代 Java 一样自然。这段代码片段就是表示 JPA 实体所需的全部内容。没有 getter,没有 setter,没有样板代码!

@Entitypublic class Person extends PanacheEntity {    public String name;    public LocalDate birth;    public Status status;}
复制代码

现在,查询数据库也很简单:

List<Person> allPersons = Person.listAll();
复制代码

Quarkus 扩展可以检测到其他扩展的存在并与之集成,减少需要编写的代码量,趋向于我们认为的最佳模式和实践。

 

例如,如果“Panache”代码片段属于一个包含数据源和健康检查扩展的应用程序,那么数据源扩展将自动为应用程序定义数据库健康检查和 Kubernetes 就绪探针。因此,当数据库不可用时,Kubernetes 将不会向应用程序容器发送流量。

 

甚至更进一步,如果同一个应用程序包含一个指标扩展,数据源扩展将自动把指标暴露给 Prometheus 这样的监测工具。

将 Kubernetes 作为一等支持平台


“这是否意味着我可以回去写代码了?我不用再处理[Kubernetes]服务配置,因为 Quarkus 帮我做了。” —— DevOps工程师,LogicDrop


Quarkus 天生就是 Kubernetes Native 的,其运行的高效率来自于 Java 字节码和本地可执行文件。此外,Quarkus 将 Kubernetes 作为一等支持平台,并提供了以下扩展和能力:


  • 配置:Quarkus 可以利用 Kubernetes API 来访问存储在 ConfigMap 或 Secret 中的配置信息,而无需将其挂载到 pod 的文件系统中。

  • 应用程序健康:使用 MicroProfile Health 向 Kubernetes 健康探测器(探测潜在的流量重定向和 pod 重启)暴露应用程序的健康状况。

  • Kubernetes 客户端:将 Kubernetes 对象封装在一个 Java 对象模型中,并提供一个 Java API 来操作这些对象。

  • 服务发现和负载均衡:应用程序可以利用 Kubernetes 集群内的 DNS 并借助 Kubernetes 的轮循负载均衡进行服务发现。或者它们可以使用 Quarkus 的 Stork 客户端负载均衡框架来实现更复杂或自定义的负载均衡算法。

  • 精简的 Kubernetes 和 Knative 配置与部署:Quarkus 会生成部署到 Kubernetes 或 Knative 所需的 YAML,后者为 Kubernetes 增加了无服务器功能。可以使用 Quarkus 属性自定义 YAML 的生成。

  • 可观察性:使用 Micrometer 或 MicroProfile Metrics 深入了解正在运行的应用程序,并使用 MicroProfile OpenTracing 或 OpenTelemetry 跟踪多个服务的请求。

  • 函数即服务(FaaS):Quarkus 提供了 Funqy,作为开发可移植函数的 API。这些函数可以在 Knative 上借助 Knative 事件运行,也可以并在 AWS Lambda、Azure Functions 和 Google Cloud Functions 等 FaaS 环境中运行。

  • 远程开发:Quarkus 为远程实时编码提供了开箱即用的支持,而且无需额外的工具。你可以对运行在 Kubernetes Pod(或其他远程环境)中的应用程序进行实时编码——不需要专门的 IDE 工具

  • Quarkus Operator SDK:简化了用 Quarkus 编写 Kubernetes 操作符的工作。

Quarkus 入门


“我能够非常迅速地准备好 Quarkus 并运行。Quarkus 指南提供了很大的帮助,因为它们专注于一个主题,而且直接了然。” —— Victor Gallet,高级开发工程师和Kafka全能专家


要想了解更多的信息,最好的方法是创建你的第一个 Quarkus 应用程序。只需要几分钟就可以,只有四个步骤!请查阅入门指南,它将引导你完成每个步骤。Spring 开发者可以通过阅读免费电子书Quarkus for Spring Developers快速掌握 Quarkus。

 

之后,你可以使用我们的网站code.quarkus.io:只要选择你想要的扩展,并生成项目就可以了。



小结

Kubernetes Native Java 关乎重新定义使用 Java 包含 Kubernetes 模型的方法,在共享环境中,通过减少启动时间和内存使用率、提高资源效率来降低成本。Quarkus 通过将大部分的启动处理转移到构建时来实现这一目标。其结果是一个高效的运行时,通过资源效率显著提升的应用程序大幅降低云计算成本。此外,企业可以提供直接影响业务的新功能,而又不会超出他们目前的云计算预算。

 

为了最大限度地发挥原生编译的优势,Quarkus 实现了与 GraalVM 的紧密结合。应用程序经裁剪后,可以充分利用提前编译带来的优化。Quarkus 使用 GraalVM 的扩展模型增强了这一过程,并避免了额外的配置。

 

Quarkus 提供的“开发乐趣”使开发人员通过实时编码、开发服务和持续测试等功能持续参与解决业务问题。使用 Quarkus 进行开发的效率非常高,Java 开发者可以尝试多种解决问题的方法,并选择对业务最有利的一种。

 

Quarkus 还将 Kubernetes 视为一等支持平台。它提供了一系列 API 框架,使应用程序能够充分利用 Kubernetes 提供的东西。这实现了 Kubernetes 投资的最大化,并通过平台整合缩短了上市时间。

 

开始吧

 

作者简介:


Jason Greene 是红帽公司的杰出工程师兼应用运行时首席架构师。他是 Quarkus 的联合创始人和共同负责人。该项目旨在革新云端微服务和无服务器应用程序 Java 开发。在此之前,他曾领导 WildFly(业界领先的开源 EE 应用服务器,正式名称为 JBoss AS)项目长达近十年。在红帽任职期间,他曾参与许多中间件领域的工作,包括服务器运行时、集群、Web 服务、AOP 和安全。他是开源的坚定支持者,在职业生涯中为许多项目做过贡献。他也是 JCP 和 Eclipse Jakarta 的成员,他曾代表红帽公司参加 EE 专家组及相关标准的制定。他的兴趣包括并发性、分布式计算、硬件和编程语言设计。


John Clingan 是红帽公司的高级首席产品经理,专注于标准和下一代平台,如 Quarkus。他也是 MicroProfile 的共同创始人和指导委员会目前的负责人。John 有 30 年的行业经验(他年纪有点大了),包括软件开发、系统管理、技术销售和咨询,以及产品管理。他还有 10 多年的授课经验,为加州州立大学和加州大学继续教育学院教授 Java 课程。最近,他还为 O'Reilly 开展了 MicroProfile 和 Quarkus 的现场培训。John 是“Kubernetes Native Microservices with Quarkus and MicroProfile ”一书的作者之一。


Eric Deandrea 是红帽公司的高级首席开发大使,专注于应用开发技术。Eric 有超过 22 年的经验,为金融服务和保险行业设计和制定基于 Java 的解决方案和开发培训计划。他也是各种开源项目的贡献者,包括 Quarkus 和 Spring。最近,Eric 将他的 Quarkus 和 Spring 知识付诸实践,出版了他的第一本书 “Quarkus for Spring Developers”。他喜欢用以 DevOps 为中心的自动化技术来简化相关工作。工作之余,Eric 喜欢在新罕布什尔州的湖上划船。他还喜欢冰上曲棍球和武术,并有空手道黑带。

 

原文链接:

Kubernetes Native Java with Quarkus

2022-05-02 08:004903

评论

发布
暂无评论
发现更多内容

企评家帮你打造企业价值评估,全面展示企业实力地位完善企业管理

企评家

时序数据库在核电安全领域的应用

CnosDB

IoT 时序数据库 开源社区 CnosDB infra

从“打地鼠”到“造城池”,华为应用市场带来的安全隐私保护新范式

脑极体

Laxcus 6.0正式发布,启航国产化分布式操作系统新征程

LAXCUS分布式操作系统

数据库 大数据 分布式 操作系统 集群

Java并发JUC(java.util.concurrent)集合不安全

芝士味的椒盐

Java java集合总结 java集合使用 Java集合框架

字节跳动Java后端岗,三轮面试,四个小时,灵魂拷问,结局我哭了但下次还敢

Java架构追梦

Java 程序员 后端开发 字节面试

立足西安,昇腾AI为开发者留下了脚印一串

这不科技

Java并发JUC(java.util.concurrent)线程池

芝士味的椒盐

Java SE Java多线程 Java 线程

如何做好高并发系统设计,我总结了三点

架构精进之路

架构 高并发 4月日更 4月月更

linux之systemctl命令

入门小站

Linux

免费ETL批量调度,数据仓库运维工具TASKCTL 8.0 环境使用安装

TASKCTL

大数据 DevOps 敏捷开发 ETL 自动化运维

企评家|高升控股股份有限公司成长性报告简述

企评家

企业评价 企业成长性 评价分析报告

Curve 基于 Raft 的写时延优化

网易数帆

开源 分布式 云原生 raft 存储

Next.js 在 Serverless 中从踩坑到破茧重生

CODING DevOps

Serverless 云开发 Cloud Studio 云端工作台

“消息驱动、事件驱动、流 ”基础概念解析

阿里巴巴云原生

阿里云 云原生 事件总线 EventBridge

暴打力扣:王者级《数据结构与算法笔记》,一路绿灯进字节Java岗

Java架构追梦

Java 算法 java面试 后端开发

共建开源组件生态 2022 OpenHarmony组件大赛等你来

科技汇

MOSN 1.0 发布,开启新架构演进

SOFAStack

开源 开发者 云原生 资讯 Go 语言

火山引擎荣获中国信通院“2021边缘计算融合创新奖”

火山引擎边缘云

边缘计算 中国信通院

SIG 直播:如何使用 SMC-R 透明加速 TCP 应用? | 第15期

OpenAnolis小助手

TCP 龙蜥大讲堂 SMC-R SIG双周会

云VR的未来发展方向

3DCAT实时渲染

云VR

火山引擎联合中国信通院发布《边缘云总体技术与测试要求》

火山引擎边缘云

边缘计算 标准化

CI/CD概述,基于云效进行持续集成CI和持续部署CD(K8s)

阿里云云效

云计算 阿里云 持续集成 CI/CD 持续部署

Kruise Rollout: 让所有应用负载都能使用渐进式交付

阿里巴巴云原生

阿里云 开源 Kubernetes 云原生 OpenKruise

中国科技人,在残酷春天里翘首等待

脑极体

DAYU200升级系统

坚果

4月月更

一文简述:何为分布式应用服务平台

穿过生命散发芬芳

4月月更

超全MySQL笔记整理(面试题+笔记+思维导图),面试再也不怕被MySQL难倒了

Java架构追梦

Java java面试 后端开发

企评家帮助新三板企业进行投资价值评价分析

企评家

10个经典场景带你玩转SQL优化

爱好编程进阶

Java 程序员 后端开发

2021,你还在写“赤裸裸

爱好编程进阶

Java 程序员 后端开发

为 Java 提速:用 Quarkus 开发 Kubernetes Native Java 应用_编程语言_InfoQ精选文章