融合OpenJDK和NodeJS:跨语言交互和垂直架构

哈Ha!

很长时间以来,我一直想与您讨论GraalVM ,直到我最终找到今天的文章为止,我的想法一直搁浅 ,该文章的主题严重超出了特定虚拟机的范围。 作者Mike Hearn概述了多语言交互和多语言编程(多语言编程)的整个范例。 接下来是垂直缩放的著名示例,并且裁切后的文章非常长。



本文介绍了一种创新的软件编写方法,该方法可能会在将来(但现在可能不是现在)流行。 老实说,这篇文章有一个代码!

在远古时代,即在2015年,我写了为什么Kotlin将成为我的下一种编程语言 ,在2016年,我写了有关Graal和Truffle的文章 :两个与编译器相关的激进研究项目,它们不仅极大地加速了Ruby等语言的工作, ,但实际上也体现了无缝的中介语言互动。 在这些项目中,动态(JIT)编译器或OpenJDK被新的替换,该新功能可以将带注释的解释器自动转换为尖端的JIT编译器。

回到2019年的这些话题,我想向您展示三件事:

  1. 如何使用我编写的小型库几乎可以用Java或Kotlin编写的程序代码无缝地使用NPM模块。
  2. 解释所有可能的理由,即使您认为JavaScript / Java是世界上最糟糕的事情,也不算鱼油。
  3. 简要探讨与面向微服务的设计竞争的垂直架构的概念。 它位于最新版本的GraalVM和OpenJDK的交叉点,并且需要最先进的硬件。

使用Java和Kotlin的NPM


我们将仅采取三个简单步骤:

  1. GraalVM为例 。 这是一组基于OpenJDK构建的补丁,这些补丁及时出现:它可以执行您拥有的所有JVM字节代码。
  2. 我们从github上获取我的NodeJVM工具包,并将其添加到我们的路径中。
  3. 在命令行nodejvm java替换为nodejvm 。 仅此而已!

好吧好吧 我承认,这里我有点夸张,直到文章结尾您必须容忍这种风格。 当然,一切都不是那么简单:您仍然必须使用该模块并使用它。

考虑一下它的样子:



使用NodeJVM的示例代码

仔细看看这张照片。 是的,这就是它的样子:Kotlin具有内置的多行字符串,其中会发生JavaScript自动补全,然后执行静态JavaScript分析,并正确突出显示语法。 IntelliJ可以从Java或其他语言对JVM进行相同的操作。 为了获得这种机会,您需要单击IDE设置中的开关(有关如何执行此操作,请阅读NodeJVM的自述文件),但是稍后此功能将自动运行。 如果IntelliJ通过分析数据流设法确定您的字符串最终应传递给runeval方法,则它将被视为嵌入式JS。

在这里,我将讨论Kotlin的API,因为它比普通Java中的API更漂亮,更方便,但是我在下面描述的所有内容也可以通过Java完成。

在上面的代码中,请注意以下几个功能:

  • 要访问JavaScript,您必须使用nodejs {}块。 事实是JavaScript是单线程的,因此,要运行NPM模块,您需要“输入Node流”。 无论我们处于哪个线程中, nodejs {}块都会为我们执行此类同步。 因此,您需要不断记住:要运行任何JS代码,原则上,您需要位于这样的代码块中。 您可以根据需要多次输入它,因此可以在我们需要的任何地方使用这种积木。 任何JavaScript回调都将在Node线程中执行,因此,所有其他线程将被拒绝访问nodejs块,因此,如果您担心性能或平滑的GUI呈现,请避免在回调中执行长时间运行的操作。
  • 语法var x by bind(SomeObject())仅在nodejs块中可用,并允许您连接到全局JavaScript作用域中的相同变量。 当x从Kotlin更改时,它将在JS中更改,反之亦然。 在这里,我将常规Java File对象附加到JS世界。
  • eval方法返回...但是,我们要求它以静态类型的方式返回。 这是一个通用函数,只需指定我们分配给它的实体的类型,即可确保eval自动将JavaScript对象转换为静态类型的类或Java / Kotlin / Scala / etc接口。 尽管上面没有明确说明,但MemoryUsage是我定义的简单接口类型,它具有rss()heapTotal()函数。 它们映射到同名的JavaScript属性,并将它们应用于从Node API process.memoryUsage() 。 因此,大多数JS类型都可以转换为“普通” Java类型。 GraalVM网站上提供了有关其工作原理的详细文档。 结果对象可以存储在任何地方,但是其中的方法调用当然必须在nodejs块中完成。
  • JavaScript对象也可以认为是字符串到对象的简单映射,在许多方面都可以真正匹配其性质。 反过来,字符串到对象的这种映射可以带回某种更强类型的东西,这可以在回调中清楚地看到。 使用您喜欢的演示文稿。
  • 您可以使用require ,它将以通常的方式在node_modules目录中搜索模块。

上面的代码段使用DAT协议,该协议允许您连接到与BitTorrent远程相似的对等网络,然后查找具有所需文件的对等网络。 我以DAT为例,因为它是(a)去中心化的,因此是独特的,(b)是好是坏,所以它的参考实现是用JavaScript编写的。 这不是我在任何合理的时间不使用JS即可完全编写的程序。

这也可以从Java完成:

 import net.plan99.nodejs.NodeJS; public class Demo { public static void main(String[] args) { int result = NodeJS.runJS(() -> NodeJS.eval("return 2 + 3 + 4").asInt() ); System.out.println(result); } } 

Java API不能像Kotlin API那样为您提供令人愉快的变量绑定和自动广播,但是它非常易于使用。 在这里,我们展示了如何将结果转换为整数类型Java( int )并从Node流“返回”:在这种情况下,主要Java流 NodeJS流不同,但是我们在这些流之间完全无缝地切换。

NodeJVM是GraalVM之上非常小的包装。 它添加了很少的代码,因此不必担心它可能会停止受到支持或消失:在这种情况下,所有辛苦工作的99.99%由GraalVM团队执行。

以下是一些建议改进的明显思路:

  • 允许JS模块通过Maven坐标导入Java模块。
  • 制定一些“最佳实践”来完善NPM模块。 例如,一个JAR文件是否可以包含一个node_modules目录(简而言之:否,因为NodeJS仍以自己的方式组织文件I / O,并且不了解zip,很长:是的,如果您努力的话)。
  • 更多语言:Python和Ruby不需要NodeJS所需的“胶水”来进行线程同步,因此您只需使用常规GraalVM Polyglot API即可 。 但是Kotlin用户会发现,使用任何语言都可以使用强制转换/扩展方法和用于绑定变量的API。
  • Windows支持。
  • Gradle插件,以便程序可以具有混合语言集的依赖项列表
  • 与本native-image工具(所谓的 SubstrateVM; 因此,如果您在运行时不需要完整的HotSpot性能,则可以提供Golang样式的小型静态链接二进制文件。
  • 也许是用于将TypeScript转换为Java的某种转换器,以便您可以使用DefinitelyTyped并快速陷入静态世界。

欢迎打补丁。

你为什么需要这个?


也许您已经在思考:“哇,JavaScript,我们,发达的,现在可以彼此相爱,相互尊重和和谐!”



理想化的热情反应

您很可能会更接近这种观点:



JavaScript和Java不仅是语言。 这些是文化,对开发人员而言,没有什么比文化战争更甜蜜了!

这就是为什么您至少应该将此页面加为书签,以备将来参考,即使您只是想仅仅凭$ OTHER_LANG入侵您宝贵的生态系统也可以:

  • 如果您主要是Java开发人员,那么现在您可以访问在JVM中可能没有等效功能的独特JavaScript模块(例如,DAT协议)。 您可以喜欢它,也可以讨厌它,但是事实仍然存在:许多人编写了开源NPM模块,其中一些模块非常好。 您还可以重复使用在Web前端上运行的代码,而无需使用语言传输器。 而且,如果您要使用NodeJS中的继承代码库(您希望逐步移植到Java),那么突然之间,这项工作将大大简化。
  • 如果您主要是JavaScript开发人员,那么现在您可以轻松访问独特的JVM库,该库在JavaScript中可能没有直接等效的库(例如LuceneChronicle Map ),或者可能只提供了文档记录不充分,不成熟或生产率较低的类似物。 如果您想在下一个项目中不使用HTML,那么您可以探索白人GUI框架 。 您还可以访问许多其他语言 ,例如Ruby和R。可以使用共享内存中的多线程在 NodeJS员工之间共享JVM对象,如果根据您的分析器,可以利用此机会。 而且,如果您要使用继承的Java代码基础,并且希望逐步移植到NodeJS,那么突然之间,这项工作将大大简化。
  • 如果您一次学习所有语言 ,则可以进行多语言编程。 懂多语言的程序员并不讨厌,相反,无论来自什么文化背景,他们都能以可用的最佳代码结交朋友。 他们就像文艺复兴时期的学生一样,他们立即学习了英语,法语,拉丁语...所有这些语言都是他们的一种。 他们混合使用Java,Kotlin,JavaScript,Scala,Python,Ruby,Lisp,R,Rust,Smalltalk,C / C ++,甚至是FORTRAN库,在GraalVM之上纯粹拼接一个整洁的整数。
  • 最后,如果您是快乐的NodeJS用户,并且其他语言完全不会打扰您,那么您可能仍想尝试GraalVM。

NodeJS基于V8,这是一个虚拟机,旨在使用可在PC和智能手机上运行的短期单线程脚本。 这正是Google资助的资金,但V8也在服务器上使用。 OpenJDK已经在服务器上进行了数十年的优化。 最新版本包含ZGCShenandoah ,这两个垃圾收集器可将延迟降至最低,而这些工具可让您消耗TB的内存,而暂停时间仅为几毫秒。 因此,您甚至可以通过使用出色的GraalVM基础架构和工具来降低成本,而无需放弃单一语言



查看包含Ruby对象的堆



可通过HTTP获得的CPU指标



超深度专家诊断,演示如何优化代码

垂直架构


我们来到了本文中我要讨论的最后一个主题。

有时我会告诉以上所有内容,但他们会回答我:“ 这很棒,但是不是所有这些微服务都为我们提供了这一切吗? 大惊小怪是因为什么? 很难理解为什么我这么喜欢多语言编程,但是事实是,在我看来,微服务架构需要健康的竞争。

首先,是的,有时您需要在需要交互的各种服务器上驱动许多服务。 我在Google工作了7年以上,几乎每天都与他们的容器协调器Borg打交道。 我写了“微服务”,尽管我们甚至都没有叫它们,但我还是使用了它们。 否则,就没有办法应对,因为我们的工作量需要数千台机器的参与!

但是,对于此类架构,您必须付出高昂的代价:

  1. 序列化 它立即导致生产率下降,但更重要的是,您需要不断调整至少部分键入和优化的数据结构,将它们变成简单的树。 当使用JSON时,您将失去做简单事情的能力,例如,拥有许多指向多个大对象的小对象(为避免重复,您必须使用自己的索引)。
  2. 版本控制 这很复杂。 大学通常不会在软件工程领域教授这一困难但日常的学科,即使您认为您已经完全理解了直接和向后兼容性之间的区别,即使您确定了解多阶段滚动是什么,也可以保证取代您的人会理解所有这些内容吗? 您是否针对非原子推出期间可能产生的各种版本组合正确执行集成测试? 在分布式体系结构中,我看到了几次真正的灾难,这些灾难归结为版本误入歧途。
  3. 连贯性 。 在同一服务器内执行原子操作非常容易。 当系统中包含许多机器时,要确保在任何情况下的用户都能看到绝对一致的画面要困难得多,尤其是当它们之间发生数据分片时。 这就是为什么历史上关系数据库引擎无法拥有良好的可伸缩性的原因。 让我告诉你: 最好的Google工程师花了数十年的时间试图简化其团队的分布式程序设计,并设法使其看起来更像传统程序。
  4. 重新实现 。 由于远程过程调用非常昂贵,因此您不会进行很多此类调用,除了重新实现代码之外,无需解决任何问题。 Google制作了一些库,可同时使用多种语言,旨在调用远程过程。 在某些情况下,此类代码也必须从头开始重写。

那么有什么选择呢?

简单地说,很多铁。 这种方法看起来很荒唐,但是请记住,硬件成本在不断降低,许多工作负载不被称为“全局全局”,而您对应该花什么的直觉会使您失败。

这是一家加拿大制造商的相对较新的价格表



如今,一台四十核的机器具有TB级的RAM,硬盘上的容量几乎为TB级,价格约为6,000美元。 想象一下,您的团队在整个项目生命周期中需要花费多少时间来解决分布式系统的问题,以及花费多少时间。

是的,但是今天不是所有的公司都基于全球网络吗?

简而言之,没有。

世界上到处都是符合以下条件的公司:

  • 他们在稳定的市场中运作。
  • 他们靠卖东西赚钱。
  • 因此,他们的客户群是从几万到几千万的人,而不是数十亿。
  • 他们的数据集通常与他们自己的客户和产品相关联。

这样的公司就是一个很好的例子。 银行不会经历“过度增长”,也不会变得“病毒式”。 如果假设他们有任何类型的增长(银行是区域性的并且通常在饱和的市场中运作),它们的增长模型是适度且可预测的。 美国最大的银行的客户群大约有5000万用户,当然,每六个月不会翻一番。 在这种情况下,情况与Instagram完全不同。 因此,难怪大型机仍基于典型的银行系统吗? 当然,物流公司,制造公司等也是如此。 这是我们经济的基础。

在这样的业务中,仅通过一台大型机器的资源就可以始终满足与它们相关的每个特定应用程序的需求。 是的,即使是今天的某些公共场所也只能安装在一台计算机上。 2015年,Maciej Tseglovsky就“ 网站肥胖带来的危机 ”发表了非常有趣的演讲,并指出他自己的带有书签服务的网站是有利可图的,但他的竞争对手在AWS上发布了同一网站-但由于成本不同而丢失了设备和有关复杂性的各种假设。 在一项比较垂直和水平缩放比例的研究中,发现PlentyOfFish大约在一个大型服务器上运行(该文章的日期为2009年,因此您可以忽略此处列出的设备价格)。 作者进行了一些计算,结果表明一台服务器并不像看起来那样愚蠢。 最后,如果您正在考虑Hadoop和大数据,请阅读这篇2013年Microsoft的研究文章,文章表明Microsoft,Yahoo和Facebook的许多Hadoop工作负载实际上在一台大型计算机而不是群集上运行得更快,更高效。 大约6年前! 从那时起,对垂直缩放的重视可能变得更加明显。

但是,真正的节省根本与设备无关,而与工程师极其昂贵的工作时间的优化有关,后者花费在创建一堆必须通过弹性需求管理水平扩展的微型微服务上。 即使您使用云中可用的最新玩具,这种工程方法也是冒险且耗时的。 您可能会丢失SQL,SOLID事务,统一配置文件,并且肯定会丢失诸如跨系统堆栈跟踪之类的信息。 每当您超出服务器范围时,类型安全性就会消失。 您将收到对可能超时的函数的调用,与动态编译相关的过多开销,意外的背压故障,具有精美配置格式的复杂编排引擎,以及……哦,内存真的满溢了。 当我拥有专有的Google体系结构和庞大的工程预算时,使用所有这些功能进行工作很有趣,但是今天,我只有在没有其他选择的情况下才敢重复此操作。

根据经验,不可能使用在垃圾收集上运行的超大型服务器-事实是垃圾收集本身是一项开发较差的技术,因此,该主题长期以来一直纯粹是学术性的。 无论如何,您必须一次驱动多台服务器。 , ZGC Shenandoah, , 80 – . , -, – , .

, ? – , ? – : , , … .

结论


NodeJVM – , GraalVM. NPM Java/Kotlin, JS , Kotlin JavaScript, JS , V8.

, JS Java, Java JS .

, 4 , – – , . .

Source: https://habr.com/ru/post/zh-CN481104/


All Articles