Java和AWS Lambda--最好的敌人?

535 阅读16分钟

*特邀帖子提醒! Mike Roberts曾是一名工程师,也是一名CTO。他是这本O'Reilly书的作者之一,也是Symphonia的合伙人。他的博客在blog.symphonia.io/。

...

AWS Lambda是引入 "无服务器 "概念的云服务。没有机器或操作系统需要管理;自动和无忧无虑的扩展;只有在做任何有用的事情时才花钱;此外还有更多。Lambda是在2014年推出的--从那时起,了解情况的人开始意识到Lambda是一种比许多替代方案更有效、更轻松的云端运行代码的方式。

我第一次使用Lambda是在2015年。当时我正在管理一个团队,该团队有一个非常大、非常昂贵的Java应用程序,每天要处理几千万条信息。问题是我们的应用没有足够的扩展性,部署起来非常慢,而且一旦出了问题,就会让人非常头疼。我们需要替换公司生态系统中的一个关键部分,但只有一个骨干人员。我们该怎么做呢?

我们尝试了一个实验。由于我们没有什么业余时间来管理系统,我们决定依靠亚马逊。我们需要一个能够处理许多信息的服务,以及一个能够有效处理振荡扩展需求的计算平台。我们选择了Kinesis和Lambda,由于我们的团队是一个使用各种Java虚拟机(JVM)语言的团队,我们选择了Lambda的全新(当时)Java支持。

结果是一个巨大的成功。当然,Lambda在当时有一些粗糙的边缘,但我们对传统应用程序的头痛问题已经解决了。我和负责设计该系统的约翰-查平对亚马逊的 "低接触 "服务的能力印象深刻,因此我们决定开始自己的咨询业务--Symphonia--我们可以在这个勇敢的软件架构新世界里帮助别人。

我们的Lambda+Java实验已经进行了6年多,其中有一个方面甚至在无服务器的信徒中仍然引起争议--Java。许多人认为Java不适合Lambda,而其他人(比如我)则认为它在Lambda领域有自己的位置。那么,你应该为你的Lambda应用使用Java,还是应该避开?在这篇文章中,我希望能让你自己决定。

Java和Lambda的 "问题"

我听到过很多关于不要在Lambda中使用Java的论点,但基本上归结为以下三个。

  1. 启动时间太长
  2. 没有人再写Java了
  3. 对于小型Lambda函数来说,Java太过冗长。

作为一个与人合写了关于使用Java和Lambda的书的人,这些观点有点让人痛苦。我为什么要花这么多时间在这么多字上?

也许在放弃希望之前,值得深究一下。

冷启动冻结

当人们说 "启动时间很糟糕 "时,他们指的是Lambda "冷启动"。了解冷启动的细节需要一篇文章,甚至一本书(咳咳),但简单地说,Lambda平台偶尔会在需要时 "冷 "启动Lambda函数的新实例,但该平台是在事件发生时及时启动,而不是预先启动。这意味着有些事件会出现额外的延迟。

Java和冷启动的问题是,启动一个Java进程要比(比如)Javascript、Python或Go进程花费更多时间。

然而,在这一点上,有几个相关的问题是:"Java有多慢?"和 "这有关系吗?"。对于第一个问题,我在几年前做了一些研究,结果是 "通常要慢四分之一到半秒"。

第二个问题则要主观得多。在2015年的时候,冷启动要比现在慢很多,但是在我们的Kinesis+Lambda应用中,我们处理的消息已经至少有一分钟了,而且我们对多等几分钟处理一个消息也没有意见。因此,对我们来说,即使是几秒钟的冷启动也没有问题。

另一个例子--三年前我和一家中等规模的社交网络公司合作,他们正在将他们的一些Scala代码(在JVM上运行)转换为Lambda。他们非常担心冷启动的问题,因为他们通常希望得到亚秒级的响应,但他们在开发中的冷启动时间超过了一秒(这并不奇怪,因为运行Scala而不仅仅是vanilla Java所带来的额外负担)。然而,事实证明,在生产中,他们的Lambda函数被频繁地触发,其99.99%的响应时间已经足够。虽然冷启动时间是一样的--一到两秒,但每10万个请求才会发生一次。对于这个团队来说,即使有几个lambda调用很慢,他们的总体性能也是完全可以接受的。

所以,是的,Java在启动时比其他语言要慢。但这是否重要取决于你正在构建什么,以及你的性能要求是什么。

还有一些技术可以在你处于边界线上时有所帮助,但我稍后会说到这些。

还有人在写Java吗?

看看大多数创业公司,或者关于新软件工具的文章,你会认为现在所有的软件都是用Javascript/Typescript、Python、Go或Rust编写的。我并不反对这些语言,实际上,我最近是Typescript的粉丝。但有时互联网上的时尚部分并不能说明全部情况。

根据各种报告,世界上大概有500万到1000万的Java开发者,而且Java仍然是最经常使用的五大编程语言之一。我觉得这些数字很有趣,但我也对产生这些数字的调查抱有一定的怀疑态度。对我来说更实在的是,AWS本身就是Java的巨大用户。他们不仅生产自己的免费Java发行版,而且他们自己也使用Java作为Lambda服务的一部分!因此,人们仍然在编写Java。

因此,是的,人们仍然在编写Java--事实上,他们中的许多人都在编写。他们只是可能没有像其他人对其他语言那样大声疾呼。

Public static void main, XML, etc. 等等。

许多写过Java的人都记得它非常冗长,至少与更现代的语言相比是如此。这部分是因为Java语言在00年代末/2010年代初陷入了低谷。但即使在今天,即使在最近几年对语言进行了重大改进,与其他语言相比,Java仍然有简洁的问题。比如说。

  • Java是一种静态类型的语言,而其他语言没有强制性的静态类型
  • 一些社区 "标准 "导致了不幸的错综复杂的命名惯例。如果我再也看不到AbstractWidgetControllerInterfaceFactoryFactory,我将是一个更快乐的人。
  • 事实上的Java构建工具(Maven)使用XML,这比YAML或JSON更不容易写。

所有这些加起来的 "重量 "使Java对小型Lambda函数来说变得不容易操作。

然而,大量的Lambda函数实际上并不小--你可以在15分钟内用6个CPU核心和10GB内存做很多事情。此外,那些习惯于使用Java的团队也习惯于它的缺点,如果有好的IDE和工具,实际上通常不会比其他软件团队差。

因此,虽然我很同情那些第一次从vanilla Javascript到Java的人,但我也认为对于许多有Java经验的团队来说,额外的口头禅在大多数情况下并不是一个问题。

什么时候在Java中使用Lambda的经验法则

我现在写的大部分代码都不是Java,但我仍然认为Java有时是Lambda应用的一个好选择。特别是当一个精通Java的团队在考虑转换到Lambda时,我更是这样认为--我认为他们不需要同时学习一个新的平台和一种新的语言。

但肯定有一些场景,Java比其他更适合。因此,为了更加规范,我建议团队在以下一些或所有情况下使用Java。

#1 - 当一个团队已经在编写Java时

如果一个团队已经在编写大量的Java(或另一种JVM语言,如Scala、Clojure或Kotlin),那么他们在Lambda环境中使用现有的语言技能可能会很好。在这种情况下,我之前列举的 "问题 "2和3就不适用了(我一会儿会说到 "问题 "1)。

一个Java团队甚至可以使用他们现有的代码和经验。从代码要求的角度来看,Lambda应用非常简单--主要是因为它们与Lambda平台的接口非常小(只是实现一个方法签名)。Lambda应用确实有与传统环境不同的架构要求--例如,状态管理通常需要重新思考--但通常这仍然允许业务逻辑代码的重用。

如果一个团队还没有使用Java,是否应该将Lambda与Java一起使用?我通常会说 "不",因为他们使用的语言本身在Lambda中可能就很好。但有一个地方我可能会说不一样......

#2 - 当吞吐量性能很重要时

有各种方法来衡量一个应用程序的性能。我们可能会关心延迟--系统的响应速度有多快--特别是对于面向用户界面的应用程序。但在其他应用中--特别是大型的、"后端"、消息处理应用--吞吐量要重要得多。每小时能够处理1亿个事件与2亿个事件之间的差别可能是及时完成工作的差别。

在这种情况下,选择一种足够快的技术是很重要的。Lambda在这些场景中经常是非常出色的,因为它可以不费吹灰之力就能扩展到很大范围。但如果每个事件本身需要复杂的处理,那么实际的运行时性能可能也是你所关心的。在这种情况下,Java是一个令人信服的选择--现代JVM的速度非常快,一旦预热就可以与本地代码相媲美。在与Python等解释型语言比较时,这一点尤其明显。

正因为如此,当这种性能至关重要时,将Java与Go、Rust等其他语言一起扔进可能的语言组合中是很有用的。一个研究这类问题的团队很可能已经有了至少其中一种语言的经验,但如果他们只习惯于使用Python(比如),那么我建议他们可能要为这类应用选择另一种语言。

但既然我在谈论性能,我也应该谈谈硬币的另一面......

#3 - 当冷启动对延迟的影响并不重要时

早些时候我描述了Java如何更容易受到冷启动的延迟影响的问题。我还说过,通常这并不重要,因为冷启动的额外延迟不会对应用程序产生不利影响,或者因为延迟总体上是足够的。

然而,有些时候这并不正确。一个很好的,也是很常见的例子是一个由用户界面调用的低吞吐量API。这里的 "低吞吐量 "是指每小时100个请求,或者更少。在这种情况下,平均来说,冷启动会比高吞吐量的应用程序发生得更频繁,所以会更明显。在这一点上,Java应用程序额外的250-500毫秒的冷启动时间将开始变得痛苦,特别是如果有一个 "链 "的Lambda函数参与满足用户的请求。

所以我想说,如果你编写的应用程序在生产中具有这样的性能要求,那么也许要避开Java。

另一方面,如果这个性能要求不成立,那么不要被冷启动吓到,而是要通过测试来确定。

#4 - 编写应用程序而不是 "胶水 "脚本时

Lambda被用在各种场景中。通常它是用于 "真正的 "应用程序--处理生产数据的东西。但有时它也被用作 "胶水",例如,加载测试数据,部署一组特别细微的基础设施资源,或作为监控流程的一部分,如向Slack发布警报。

在这些 "胶水 "情况下,我通常建议不要使用Java。原因是这些小的Lambda函数通常只是由一个文件或脚本组成,说实话,一个完整的Java工具套件的负担是过重的。我发现在这种情况下,用一个小的Javascript或Python脚本代替会更有效。除此以外,你还可以在AWS Web Console中编辑它--这是用Java绝对做不到的。

有些规则是可以改变的...

如果你的团队已经熟练掌握了Java,那么在使用Lambda的过程中,最大的障碍--无论是感知的,还是真实的--往往是冷启动问题。如果这是唯一阻止你的东西,那么我有一些建议,你可能会让Java和Lambda的性能得到解决。

  • 确保你已经把 "内存大小 "的配置设置得足够高。Lambda有一个主要的 "性能表盘"--名为 "内存大小"。然而 "内存大小 "也会线性地调整你的CPU数量,从而影响你的冷启动时间。如果你的内存大小被设置为256MB,那么无论你用Java Lambda函数做什么,都会有很大的冷启动问题。因此,我建议你将内存大小设置为至少1769MB,用于对延迟敏感的应用程序,这样你就有一个完整的CPU。
  • 减少你的函数工件中的代码量。在我对Lambda冷启动的研究中,我发现对冷启动的速度影响最大的是函数工件的大小。换句话说,JVM需要加载的代码越多,它的速度就会越慢。因此,为了减少你的冷启动时间,把代码从你的工件中拿出来吧最简单的方法是为每个Lambda函数建立自定义工件,每个工件中只有每个函数所需的代码和库。如果有必要,你可以进一步使用 "摇树 "工具,它将在构建时分析你的代码,删除任何不使用的代码。不过树状摇动通常需要微调,以及彻底的测试。
  • 减少你的代码在启动时所做的事情。在启动过程中发生的很多事情都是由于 "系统 "的启动--例如,JVM的启动和加载你的代码。然而,在冷启动时,你的处理程序函数的Class也被实例化了。如果你使用任何框架作为你代码实例化的一部分,那么这也会减慢冷启动的速度。因此,我建议不要使用像Spring这样大量使用反射的框架,因为它们会给启动增加多秒。如果你仍然想使用 "依赖注入 "框架,你可以考虑Micronaut,它在构建时执行工作,而不是在JVM启动时。
  • 考虑调整JVM的设置。编写过一段时间的Java代码的人都知道,JVM有一个看似无限的配置选项来调整性能。AWS为这些设置挑选了一个标准配置,对大多数情况下都是 "足够好 "的,但也可以挑选你自己的。亚马逊的Mark Sailes最近的一篇文章挖掘了何时以及如何改变这些设置以改善冷启动。

...其他规则可以被打破

假设你真的想使用Java和Lambda,但即使你做了上述改变,冷启动仍然是痛苦的。你还有什么可以做的吗?

是的,但现在我们进入了 "这里有龙 "的领域

一种可能性是不使用普通的JVM,而是使用一个 "提前编译器",比如GraalVM。GraalVM将JVM在启动时进行的一些工作转移到构建时进行。为了使用GraalVM,你不能使用标准的Lambda Java运行时,但AWS确实提供了一个如何使用它的演示

最后一个选择是通过使用LambdaProvisioned Concurrency(PC)将冷启动从循环中移除。PC允许你指定在任何时候你想要多少个Lambda函数的实例。如果你设置了一个PC配置,那么AWS将保证在任何流量被发送到它们之前,该数量的函数实例已经被 "冷启动",因此流量不会受到启动延迟的影响。

这听起来很厉害--为什么我以前不这么说?问题是,PC也有很大的缺点:它打破了Lambda的成本模式,因为即使你的功能没有活动,你也要付费;使用PC的Lambda功能重新部署很慢(几分钟或更久);如果你的规模超过了你的PC配置,你仍然会受到冷启动的影响;如果你想 "自动扩展 "你的PC设置,这样做需要一些相当复杂的基础设施。但是,如果你真的,绝对,想消除冷启动时间,那么AWS给你提供了这样的选项。

Java和Lambda...是朋友吗?

我希望我在这篇文章中告诉你,尽管你可能听到了相反的说法,但Lambda和Java可以很好地一起工作。我不会说它们是 "闺蜜",但也许更像是 "相互尊重的同事"--在大量的工业场景中都是如此

我总结说,如果你是一个对Java有经验的团队的一员,并且你想尝试用Lambda来构建应用程序,那么就从Lambda和Java开始。感受一下它是什么样的,然后再决定冷启动是否会成为一个问题。通常情况下,它们不会成为问题,尤其是对于高吞吐量的应用程序,但如果它们是的话,我已经给了你一些关于如何弯曲(或打破)规则的想法。