现代Java平台

2021-03-17 11:42:13

在2000年代初,许多开发人员被过度复杂的Java世界烧毁。四种模式和中间件/ J2EE / Java EE的帮派导致了荒谬的据称解耦等级,因为我在2002年的开放源J2EE电子商务系统中的序列图中是显而易见的:

回到2014年我写了关于事情的变化:Java不吮吸 - 你只是用错了。但是自从我写道,六年已经过去了,事情继续改进,使Java平台建立微服务,数据流水线,Web应用程序,移动应用程序和更多时光。让我们走过一些“现代”(截至2021年)方面到Java平台。

Java是一种语言和平台。老实说,我在过去的十年里没有写过实际的Java,但我在Java虚拟机(JVM)上建立了很多。 Java语言继续发展;这由Brian Goetz(Java语言架构师)描述为“上次移动优势” - 在彻底证明的其他语言中有用后添加了功能。对于大量数百万的Java开发人员来说,创新速度缓慢是一件好事,他们能够始终专注于减少进化引起的风险。保留向后兼容性长期以来一直是java语言的重要特征,即喜欢缓慢移动,并没有任何打破。

除了Java语言本身,Java Ecosystem将跨越其他流行语言,包括Scala和Kotlin。事实上,我在过去十年中写的大多数代码都在Scala,现在是第14个最受欢迎的编程语言(根据RedMonk)。 Scala一直是我从“无分号”方法的“Java”的旅程,现在试图完全接受静态键入的功能编程。 Scala继续尝试采用即将到来的Scala 3发布的激进实验,最终验证了许多博士学位研究和Martin Odersky在可核实的微积分上基于型系统的宏伟视野。我真的很喜欢写Scala代码和持续改进。它感觉非常前沿,我的个人生产力比使用更多的古老语言更好。

在过去的两年里,我写了很多Kotlin代码。 Kotlin现在被Redmonk排名第#18,在Go的脚跟上咬紧牙关。谷歌决定使其成为Android开发的默认值,Kotlin的使用飙升。但我不做多少Android编程。 Kotlin已成为建造后端的非常富有成效的语言。语言特征如类型推断,显式无剥夺性和结构化并发(Coroutines)使其成为建立现代服务器的重要性。当我写Kotlin时,我会在Scala错过一些东西,但总体而言,我的富有成效,我的代码由Java开发人员更具吸引力。

Scala和Kotlin在Java平台生态系统上构建,具有共享工具,库等。例如,Netty是Java库,可能是通过类型处理最全局流量的HTTP服务器。它几乎在每个大型企业系统中。许多Scala和Kotlin Server框架使用Netty,因为互操作性只有作用。

如果你已经离开了Java语言一段时间并想拿到另一眼,请查看Kotlin。它有许多现代语言特征,不会感到太不同。有关一些好功能的概述,请查看我的视频:Kotlin - 更好,更云的Java。如果您想要更大的跳跃到更现代化的东西,请查看Scala,其中许多能量最近围绕写作并发和可协调的代码,可以通过编译器验证。我真的很喜欢zio,一个scala框架,为纯粹的功能计划提供了一种类似的体验。

当我在其他平台中使用开发人员工具时,我通常会失望。我不应该在我的系统上安装一些特殊的本机库,所以我可以安装依赖项。它不应该花几个小时来获得我当地的开发人员工具链设置。 IDE中提示的代码应准确且快速。构建系统应提供有关常见任务的标准方法:构建,运行,测试,调用静态代码工具,重新格式化代码和我们所有时间的其他事物。

在Java平台世界中,有一些选择工具和IDE的选项。我对Java,Kotlin和Scala的Intellij Idea有很好的经历。但VS代码也很好。在进行Java和Kotlin时,我发现Gradle或Maven Build Tools成熟,快速和全功能。在Scala我使用SBT,它提供了理想的开发人员体验。所有人都在Intellim中直接支持,以便我的构建系统和我的IDE对依赖关系一致了解。

使用数据库的应用程序通常不会使用生产数据库类型进行DEV和本地集成测试,因为它太疼痛,有时慢。这使许多开发人员使用内存数据库进行开发和测试。反过来,这导致了由于DEV数据库和生产数据库之间的差异而导致的巨大问题和限制。一些开发人员已经开始使用数据库依赖性的Docker容器来提供一致性。但这造成了处理数据库的生命周期的痛苦。 TestContainers项目通过管理数据库(或其他集装箱服务)作为应用程序生命周期的一部分来处理它的重要方法。例如,集成测试可以为一批测试,或每个单个测试用例,或所有测试,或所有测试,以及最优秀的生命周期作为测试本身的一部分进行管理。

减少时间来验证变化对于生产力至关重要。在我典型的开发人员工作流程中,我可以在几秒钟内测试我正在进行的东西。这意味着编译器运行(根据我正在使用的语言和框架,验证变化级别),我正在运行的测试,如果我正在构建Web应用程序,则服务器重新启动和我的浏览器自动 - 重新加载。除了保存文件之外,所有这些都可能发生在没有任何动作之外。 Gradle和SBT使用连续的构建/测试/开发模式支持此盒子。 Quarkus Framework还支持Maven。

在Java Ecosystem中,您只需编写直接将HTTP请求和硬线一起处理呼叫链的代码。但通常,框架通常用于简化Code​​Base的碎片连接在一起,以便重用和针对不同的环境(开发,测试,产品)。接线发生了不同的方法。 Java Ecosystem中最典型的方法称为依赖注入(DI),并通过弹簧框架推广。

春靴(“使用Spring Framework的”现代“方式)是Java Ecosystem Framework King,并与Java和Kotlin一起工作。当我用一个非功能为导向的程序员团队编写代码时,我通常会使用Kotlin和Spring Boot。代码通常很容易遵循,开发人员体验非常好。这个领域有很多替代方案,但这是两个脱颖而出,您可能想要使用它们的原因:

Micronaut - 类似的编程模型到春靴,但有几个现代曲折,如编译时依赖注入

使用Scala,还有一个大量的框架,从Spring Boot-like的范围都是非常稳定的。我一直是游戏框架的开发者生产力的粉丝,但如果你想做那种规划风格,只需使用Kotlin。 Scala真的很闪耀,因为你更深入地使用功能规划。出于这个原因,ZIO是我的主要推荐,但还没有一个完整的Web框架。有一些图书馆和基本碎片,但尚未匹配播放和春靴的端到端框架体验。解决方案是在作品中,但如果您想今天用Scala为Web应用程序进行纯粹的功能,最好的选项是HTTP4S,可以与ZIO组合。

非阻塞/反应是“现代”与传统的中央划分元素之一。传统系统通常在等待发生的事情时使用大量资源(如数据库或Web服务响应)。这显然是愚蠢的,但修复它通常需要新的编程模型,因为传统的命令代码没有暂停的方法,然后在等待结束时恢复。 Java长期以来一直没有阻止IO(NIO),大多数网络库已经使用它很长一段时间,纳尼蒂是最常用的。遗留代码基础可能仍然可以使用阻塞API,因为有反应性的复杂性障碍。直到最近没有成熟的反应数据库库。你必须一直反应,以获得足够的价值以超过成本。

春靴子和大多数其他Java和Kotlin框架已经完全拥抱了反应性,并使其成为他们“现代化”故事的中央部分。最近,即使数据库层也通过R2DBC库获得了很大的反应支持。但是,并非所有ORM /数据映射库都有支持。同样,大多数HTTP客户端库都支持反应性编程,但并非所有。

如果您使用KOTLIN,您可以利用与弹簧启动或蒙太语的并发/反应的金属版。结构化的并发模型使反应性能随着典型的命令编程而变得简单。只需确保您正在使用的库也不是封面下的非阻塞。

反应是Scala的规范,相当长的是,Scala-World中的一些人创造了反应性宣言。与大型编程社区中的一切一样,您可以做出反应的多种方式。对于HTTP客户端签出STTP,它具有支持ZIO。对于数据库客户端,我真的很喜欢Quill,它有支持各种非阻塞驱动程序。

除了典型的要求面向要求的架构(Web应用程序和REST服务),还有各种架构模式作为“事件驱动”或“流”,或者“流”,也可以消失。

Akka的演员模型是做反应/事件驱动的消息传递的一个框架。演员的核心好处之一是监督,当事情出错时,可以轻松恢复。 Akka拥有内置Java和Scala支持以及处理网络集群演员的方法。在Akka的演员之上,有一个名为“Akka Streams”的流处理框架,我在Kafka顶部的实时事件处理管道使用Scala使用Scala。

其中有各种其他流处理框架和库,包括zio流,flink,ksqldb和spark的微批化。对于许多这些方法来说,Kafka已成为典型的消息总线,因为它非常支持水平缩放,消息重放,每主题耐用性设置,以及最常见/至少一次/有效地交付。

在传统架构中,通过更新数据库进行处理,然后丢弃始发事件,使数据库源自真相。在MicroStervice架构和分布式系统中,这种方法充满了最终一致的数据,无法缩放,并且添加新数据处理客户端的问题。为了克服这些问题,已经出现了更新的架构模式,包括命令查询责任分离(CQRS),事件采购和无冲突复制数据类型(CRDT)。

CQRS从可变数据存储到不可变事件流中的源自源。这提供了更好的可扩展性,分发和附加新客户/微服务的方法。通常,您仍然需要表示从处理所有事件计算的状态的“物化视图”。 CQRS的好处是您可以通过重播所有事件来始终重新编译物化视图,但在可能需要很长时间的大型数据集中。快照是处理此问题的好方法,因为Kafka能够从给定的时间点重播,因此从快照重建物化视图是微不足道的。

CQRS的最大缺点是编程模型已经相当低。幸运的是KSQLDB和CloudState正在让事情变得更加容易。我用kotlin使用,并且有很好的经历。有关此方法的更多详细信息,请查看CloudState的博客:使用CloudState和Akka无服务器上的Google Cloud上的状态无服务器。

在J2EE / Java EE的日子里,我们称我们的应用程序服务器“容器”,因为它们运行了被包装为“Web应用程序归档”或WAR文件的Web应用程序。今天,我们仍然通常在容器中运行我们的应用程序,但它们现在是多角形,支持许多不同的运行时。用于运行容器图像的流行环境包括Kubernetes,Docker和Cloud Run。有许多方法可以在Java生态系统中创建容器图像,包括Dockerfiles,Jib和BuildPacks。有关这些方法之间的差异,请查看我的博客:比较Containerization方法:BuildPacks,Jib和dockerfile。

在创建容器时,您可以选择提供操作系统和JVM的“基础图像”。有许多不同的选项,最多是OpenJDK的变体,Java平台的开源参考实现,标准版。如果您不确定使用哪个JVM基础映像,可以尝试相当苗条的下调Distroless Java图片:

AdvenJDK的AdvicopenJDK建立也是一个不错的选择,但他们住在DockerHub上。

某些JVMS可以在容器内运行时自动选择设置。例如,Hotspot JVM根据CPU数和RAM的数量而改变它使用的垃圾收集器。要减少垃圾收集暂停,OpenJ9 JVM检测到CPU空闲,然后运行垃圾收集器。过去几年发布的OpenJDK版本自动根据容器分配的RAM自动确定内存设置,并具有分配给容器进程的CPU资源的一致视图。

Java,Kotlin和Scala都在容器中运行得很好,一些框架支持箱子外容器化:春靴集装箱化,Micraconut Containization(Gradle | Maven)和Quarkus Containization。否则,您可以轻松使用构建工具插件来创建容器图像。

JVM在优化执行时非常好,它运行的越多(或更长),它适用于您购买服务器的典型数据中心使用情况,所以即使它们的利用率很低,您也可能保持运行。在云中,我们不必这样做。相反,我们可以使用仅在使用时仅分配资源的型号。这是一些调用“无务”,它具有缺点,该服务器通常不会长时间留下运行,稍微避免了JVM的一些值。由于无服务器是基于需求的,当请求进入时,如果没有足够的备份资源来处理请求,则需要将新实例旋转。此请求击中“冷启动”,它们可以是您的P99延迟的真正拖动。

冷启动与JVM可以让用户等待一个不舒服的时间。想象一下你在购物车上点击“结账”按钮,并且在JVM启动时似乎发生了15秒钟,运行时DI注释已启动,缓存是水合的,等等。处理此操作的一种方法是不要使用JVM进行基于JVM的应用程序。什么??似乎是不可能的,但这正是Graalvm本机图像的启用。尾随时间将基于JVM的应用程序汇总到原始可执行文件中,该目的可执行文件开始于时间的小部分并使用较少的内存,但可能看不到“温暖”性能达到基于JVM的应用程序的级别。

Graalvm本土图片是魔法,但当然有一些警告。提前的汇编意味着Java中的一些其他魔法变得棘手。 Java中的许多库使用运行时的内省和修改(称为反射)来动态处理依赖注入和序列化等事物。这些动态魔法潜伏的龙不能逐步编译,因此使用Graalvm本机映像,您必须告诉它所有动态的东西。这可能有点棘手 - 可能但很棘手。主要框架努力使这种简单而有点自动,但我经常遇到问题。

一些Scala的东西真的在这里闪耀,因为功能程序员通常不喜欢动态魔法龙,因为它们本质上不是纯粹的功能。 ZIO再一次是一个很好的选择以及http4s。我在生产中有一个Graalvm本机沉着的HTTP4S服务器,以100ms开头,并具有16MB容器图像。因为我基于一些纯粹的功能基础,所以使用GRAALVM本机图像来提前编译它非常容易。

一般来说,GRAALVM本机图像的支持是改进,在接下来的几年里,我相信大多数现代Java,Kotlin和Scala程序只会在没有JVM的情况下工作。未来的汇编确实需要一些时间,所以这是我在CI / CD管道中只做的事情。我仍然使用JVM进行本地开发,以保持我的开发和测试迭代超级快速。

OpenJDK是一个常规开源项目,具有多供应商/分布式电力治理结构。 Java社区流程和JDK增强建议为任何人提供了贡献。 Kotlin和Scala都是由典型开源治理模型的基础所有。因此,在大多数方法中,核心Java平台技术就像其他免费和开放的编程平台一样。然而,有几个部分是专有的。要使用Java品牌(由Oracle拥有)标记自定义JDK,它必须通过技术兼容性套件中的测试,该试剂盒必须从Oracle获取此目的。 Java语言API也有可受版权保护的可能性。

对于任何编程平台,存在锁定的风险导致意外成本。幸运的是,存在一些现有的方法来减轻这些风险,如使用Kotlin或Scala以及AdvodoPenjdk。

Java生态系统继续在许多方向上进行创新。在语言方面,Java,Kotlin和Scala都沿不同的方向推动,但效果有些共享。例如,Scala的模式匹配可能是任何编程语言中最好的。这有助于某种方式激励Kotlin和Java的更好模式匹配。 JVMS还在垃圾收集和性能周围看到了大量的创新。当项目织机(纤维和JVM上的延续)达到成熟时,随动编程将更容易。 Graalvm是一块惊人的工程,有助于激励Java社区,以减少使用动态魔法龙(并且这是一件好事)。 Netty已经开始研究IO_ING SUPPORT(完全异步LINUX SYSCALL)。通过CRDT和CQRS分布式数据开始获取CloudState等项目的势头。还有更多! Java Ecosystem中发生了很多令人兴奋的事情!

所以你想用Java平台现代化,但是通过这种庞大的生态系统,你如何知道什么选择?我很乐意提供建议,但有这么多种不同类型的应用和许多不同的辅助因素,即真正难以提供单尺寸适合的所有指导。而是有一些问题要问:

将有一个Web,移动或其他UI共享代码与与后端相同的团队一起开发的/开发的吗?

让我在评论或通过电子邮件(Jameswark Dot Com)的电子邮件中了解这些问题的答案,我可以提供一些指导。