Java Champion Jonas Bonér 介绍 Akka 平台

作者:Janice J. Heiss

2012 年 3 月发布

本系列访谈聚集获得业界、学术界、Java 用户群 (JUG) 以及更大社区中 Java 开发人员特别认可的 Java Champion

Jonas Bonér
Jonas Bonér

Jonas Bonér 既是一位丈夫、父亲,又是一位程序员、教师、演讲者、作者和 Java Champion。他是 Typesafe 的 CTO(这家公司由 Scala 编程语言和 Akka 中间件的创始人于 2011 年创立),并且是开源社区的积极撰稿人。最著名的是,他创建了 Akka 项目(AspectWerkz 面向方面编程 (AOP) 框架),致力于 Terracotta JVM 集群技术,并且是 Eclipse AspectJ 团队的成员。Akka 中间件是 2011 年度 JAX 创新奖“最佳创新 Java 技术奖”决赛入围者之一。

Oracle 技术网:您曾表示,编写正确的并发、容错、可伸缩应用程序太困难,因为开发人员经常使用错误的工具和错误的抽象级别。对于这类情况,您能否举几个例子?

Bonér:首先我们谈谈并发。我认为,在 Java 中执行并发的默认方式,即使用所谓的共享状态并发的方式,并不是合适的默认方式。共享状态并发意味着并发访问由锁保护的共享可变状态。所有人(包括专家)都知道锁极其难以理解和正确处置。

首先,我认为基于可变状态的系统可能难以预料。这正是许多开发人员习惯于编写大量测试套件的原因 — 他们对自己的代码是如此没有把握。他们更改位于系统一部分中的内容,就可能破坏代码库的另一似乎并不相关的部分。罪魁祸首是使用了纠结不清的共享可变状态。

其次,锁难以正确处置。锁不能组合,这就导致难以设计应用程序和库。而且,采用锁的顺序也很重要,通常难以得到合适的粒度,难以了解应锁定哪个锁,且错误恢复很复杂。从可伸缩性的角度来说,使用锁通常伸缩性不大好,因为它们会阻塞线程的执行并强制切换上下文。现在,如果您将这两个问题放在一起,就会得到一个难以编写、理解和维护,也难以获得良好的性能和可伸缩性的系统。

使用锁的一个常见问题是它们太低级,因此操作系统对线程和内存屏障的处理方式一路泄漏到编程模型。有时我们确实需要在该级别进行编程,但我认为不应一直都必须这么做。要解决并发之类的难题,需要在更高的抽象层级进行一些工作。关于可伸缩性和容错,许多集群工具和平台试图跨网络仿效共享可变状态方法。我认为它们采取了错误的方法。首先,正如我们先前所述,共享状态并发(共享可变状态和锁)方法本身就会产生许多问题。如果您现在试图在像网络这样本身不可靠的系统上仿效这种方法,只会失败。这会产生一种存在泄漏的抽象,在许多情况下,其泄漏的程度太大以至于实际没有什么用。

Oracle 技术网:请谈谈 Akka 是如何解决这些问题的。

“Akka 为解决并发性、可伸缩性和高可用性问题提供了一个工具包。”

Jonas Bonér
Java Champion

Bonér:Akka 针对并发性问题提供了多种解决方案。它为解决并发性、可伸缩性和高可用性问题提供了一个工具包。它带来了某种要学习的事物和某种要使用的事物。Akka 提供了多种工具来为开发人员提供帮助。Actor、future、代理和软件事务内存共同促成了抽象层,并使我们能够更轻松地编写、理解和维护并发、可伸缩、可容错的代码。您从更高层概念(如消息流和事务)的角度来考虑,而不是在非常低层的构造上浪费时间。通常在标准企业应用程序中利用低级管道解决的问题在 Akka 中已成为工作流。因此您开始思考数据在系统中如何流动而不是如何恰当地获得并发性和可伸缩性。

Akka 中利用 actor 实现了一个什么也不共享的架构。Actor 可视作终极面向对象,其中封装了状态和行为。但与普通 Java 类的情况不同的是,每次状态更改都发生在本地,不能以任何方式影响系统的其他部分,这可解决了一个大问题。Actor 的分离程度也比标准 Java 对象大得多。如果一个 actor 崩溃(出现异常故障),它不会影响系统的任何其他部分。因此如果一个 actor 发生故障,您就知道到哪里去查看,因为故障被完全隔离。当添加容错风格的 actor(您所创建的 actor 在“监管者层次结构”树中自动互相链接)时,就得到一个可以监视和自我修复的系统,即一种非常强健的系统。

我们鼓励与 actor 一起使用的另一项技术是,使用不可变数据设计函数式程序 (FP)。在 Akka 中,您也可以获得如下好处:所有低级别、高性能的管道在一个运行时内一次性完成,并由众多用户在多种不同的环境中进行测试和强化,因此不必每个人亲自在自己的应用程序中一次又一次的重复进行此工作。

同时,在创建 Akka 时,我们希望在其核心包含分布式计算。分布式编程的本质是异步消息传递或消息传递并发性,这与只有在进程内才能可靠工作的共享状态并发性大不相同。对于 Akka,我们决定这样来做:我们让消息传递成为使用 actor 的一流构造,而不是试图将程序员与网络隔离。这样做便于了解所发生的情况,也便于推理。这样可获得一个不会撒谎的系统,这个系统不会欺骗人。您可能听人说过 actor 是一种透明的分布式计算,而实际情况恰恰相反:actor 实现非常显式的分布式计算,这样如果用在本地上下文中,它只是一项优化。

结论:使用 actor 模型,您能够以统一的方式使用单一编程模型、单一抽象和单一运行时进行伸缩 — 在一台计算机上扩展利用多核架构以及在一个网络上扩展到多台不同计算机。此外,使用 actor 模型,您对固有故障有弹性抵抗力,因此无需借助 actor 模型之外的其他手段,您就可以获得高可用性 (HA),这对于目前编写可伸缩软件非常重要。

Oracle 技术网:您曾写道,“为了实现容错,Akka 采用了‘任其崩溃’也称‘接受失败’的模型,这种模型在电信行业的运用取得了巨大的成功,构建了能够自我修复的应用程序、永不停机的系统。”您能否详细谈一谈?

Bonér:当然可以,我们已经简短涉及了这一主题,下面我们来更详细地介绍一下。当您创建标准 Java 企业应用程序时,对应用程序至关重要的状态通常分散在应用程序各处。这里一点儿,那里一点儿,此状态通常是由 try/catch 代码块来监管。这导致状态和错误恢复分散且纠结不清,并鼓励采用防御性的编程方式。在 Akka 中,您在监管者层次结构中设计应用程序。当您从 actor P(用于父级)内创建 actor C(用于子级)时,P 成为 C 的父级和监管者。这意味着如果 actor C 因异常出现故障,actor P 将收到通知并可采取行动,如重新启动 actor C。应采取何种实际行动是通过声明方式配置的。

这样您就得到了一个层层监管的 actor 树,即监管者层次结构。这产生的是一种非防御性的编程方式,在这种方式下,当一个 actor 发生故障时,该 actor 直接挂掉并由另外某个 actor(监管者)处理错误(一种快速故障处理方法)而不是尝试捕获错误并恢复。如果监管者不能从错误中恢复,则沿着层次结构向上传递将其升级,希望在层次结构中某个更上层的 actor 配置为处理该错误,否则整个应用程序将失败。

例如,在 OutOfMemoryError 的情况中,没有什么可做的,应用程序将不得不退出。但 actor 监管可以跨 actor 应用程序和跨物理计算机工作,因此鼓励让监管者层次结构跨多台计算机以便应用程序可以从整个节点故障恢复。结果就得到了一个能够自我修复、能够在运行时自愈的系统,并为开发容错能力非常强的系统打下了基础。

通常设计基于 actor 的系统的方式是使用一组顶级 actor,其中置入称为错误内核 (Error Kernel) 的最关键的状态。然后添加保护错误内核的层层防护,就像层层叠叠的洋葱皮一样。例如,如果错误内核中的一个 actor 需要执行一个可能失败的操作,它不会自己来做,而是将工作委托给位于其防护层中的一个 actor(这个 actor 也可以委托给自己的防护层,等等)。如果操作失败且工作 actor 挂掉,错误内核仍会正常运行,应用程序可以继续正常工作,就像什么都没有发生一样。

Oracle 技术网:Oracle 开发人员 Brian Goetz(《Java Concurrency in Practice》的合著者)表示,“通常在 Java 应用程序中编写快速代码的办法是编写傻瓜代码,即直接、干净并遵循最明显的面向对象原则的代码……因为编译器是由人编写的,而人要受到计划和时间预算的限制,因此编译器开发人员将他们的精力集中在最常见的代码模式上,因为正是这些地方能让他们获得最大成效。因此如果您使用直接面向对象的原则编写代码,那么比起编写那些看起来很聪明但编译器却无法有效优化的盘根错节、破解的位拆裂 (bit-banging) 代码来说,您能够获得更好的编译器优化。因此,干净的傻瓜代码通常比真正聪明的代码运行得更快。”

您对此作何感想?

Bonér:绝对如此,至少在一般意义上来说。我曾经做过 JRockit 方面的工作,因此我知道聪明的代码和坏代码都可能让优化编译器实在难以完成工作。但以我的经验,编写极高性能的 Java 代码有时需要放弃标准、简单的途径。例如,在 Akka 中,我们不得不求助于使用“sun.misc.Unsafe”,因为使用 Atomic*FieldUpdater 会使计算机代数系统产生太多开销,且因为 AtomicReference 及友元引入了太多间接方式和假共享。另一个示例是使用 ByteBuffer 堆外存储大量数据以避免垃圾回收。但这些是我们在内部解决的特殊问题:Akka 用户完全不需要了解内部,而且这大概不是 Brian 头脑里所想的。大量实际 actor 实现将使用非常直接的代码 — 这正是我们的目标。

Oracle 技术网:Java Champion Heinz Kabutz 表示“以我的经验,良好的面向对象的设计易于产生更快、更便于维护的 Java 代码。但什么是好代码?我发现使用良好的面向对象的设计模式更容易对“良好”进行归类。我通常鼓励软件开发公司对其所有程序员(从最初级的到资深架构师)进行设计模式培训。采用良好设计模式的团队会发现对其代码进行调优时要轻松得多,代码更为强健、更少需要复制和粘贴。”

您是否同意?

“我认为竞争是好的,这么多年以来,HotSpot 和 Oracle JRockit 团队彼此激励,不断让人们重新认识 Java 的能力范围。”

Jonas Bonér
Java Champion

Bonér:我不能肯定。首先,我认为“良好”不等于面向对象的代码。以我的经验,函数式 (FP) 代码有时是更好的选择,它可以产生更简单、更易于维护且组合更好的代码。其次,我不同意“良好”等于使用设计模式,至少在经典的四人组 [GoF] 意义上来说不是如此。在许多情况下,使用设计模式是一种解决编程语言或框架缺点的办法。一般来说,我认为夸大了设计模式的作用。

Oracle 技术网:收购 Sun Microsystems 之后,Oracle 同时拥有了 Oracle JRockit 和 HotSpot,两者各具不同的风格、假设和优势。HotSpot 既适合客户端应用程序又适合服务器应用程序,而 Oracle JRockit 针对企业应用程序体系进行了高度调优。Oracle 正在努力使用 HotSpot 代码库来融合这两者,因为在 Oracle,了解 HotSpot 代码的工程师比了解 JRockit 代码的工程师更多。Oracle JRockit 所提供的性能和可服务性特性将逐渐移植到 HotSpot。您曾在 BEA 从事 JRockit 方面的工作。您对此举有何感想?

Bonér:我认为这基本上是朝好的方向发展。Oracle JRockit 有一些非常好用的特性,例如其 Mission Control 工具集,我认为该工具集在 Java 应用程序调试方面出类拨萃。将其提供给更广大的使用群体是一件大好事。两个团队都令人惊叹,将它们合并到一起无疑将创造一个更好的产品。关于潜在的缺点,我认为竞争是好的,这么多年以来,HotSpot 和 Oracle JRockit 团队彼此激励,不断让人们重新认识 Java 的能力范围。

不要循规蹈矩

Oracle 技术网:在您的网站上,您说如果您不能解决一个问题,这就是因为您是在循规蹈矩。您能否为我们举几个开发人员因为循规蹈矩而受阻的例子?

Bonér:我们有太多时候受习惯、现有知识、“最佳实践”还有宣传的束缚了。我们需要经常训练自己跳出框框来思考。破除这些习惯的唯一方式是不断挑战自己增强好奇心、学习新事物、对已知“事实”提出质疑。大脑是有惰性的,您必须通过强迫它不能养尊处优而不断训练它。Rich Hickey 最近有一段精彩的谈话讲到“简单与轻松”,其中他将轻松 定义为熟悉。大脑喜欢“轻松”,因为它本性上从来不希望学习什么新东西。但这样的话,您就永远不可能期望自己在任何方面取得进步。Andy Hunt 的大作《Pragmatic Thinking and Learning》就是一部有关如何训练大脑的伟大工作手册,它可以一路为您提供帮助。

Oracle 技术网:请谈谈您目前所参与的开源 Java 项目。

Bonér:现在占用我大部分精力的开源项目是 Akka 和开源软件的 Typesafe 套件。大约 10 年前,我开始了第一个开源项目:AspectWerkz AOP 框架,这是一个巨大的成功,它稍后与 AspectJ 合并。我参与的另一个有趣的项目是 Terracotta JVM 集群技术。

Scala 的未来

Oracle 技术网:您在斯坦福的 Scala 日做了演讲。您如何看待 Scala 的未来?

Bonér:Scala 有一个光明的未来。我们看到它正在被各行各业的许多大型组织采用,从投资和商业银行到零售和社交媒体、模拟、游戏和博彩、汽车和交通系统、医疗保健、数据分析等等。它已从一个学术性的专用语言成长为一种可行的(在许多情况下甚至是更好的)Java 和 C# 替代品。

Scala 运行在 JVM 上,JVM 是迄今最佳的托管运行时。Scala 是一种静态类型的语言,对我而言这主要意味着两件事情:获得卓越性能(与 Java 相比),以及从编译器获得实实在在的帮助,这有助于在开发过程的早期阶段捕获问题和设计错误。这有助于更轻松地进行重构并且让代码可以不断地演变而不会变得陈腐。

尽管 Scala 是静态类型语言,但它像 Ruby、Python 或 Groovy 之类的动态类型语言一样具有表现力和简洁。它具有类型推断功能,简而言之,这意味着您不必一直与编译器合作,像在 Java 中常做的那样在所有地方都声明类型。它可以从上下文得出自己的结论,这使得您可以专注于本质。

Scala 是一种用于构建高吞吐量、低延迟系统的完美系统语言,正是因为这一点,我们才将其用作 Akka 中间件的基础。Scala 是一种用于构建并发和并行系统的卓越语言,为此它提供了以下特性:用于自动算法并行化的并行集合、用于消息传递并发的 actor、用于执行非阻塞未来组合的库以及即将推出的 STM 库等。现在使用 Typesafe,我们可以在 Scala 中提供一流的支持以及围绕它的完整开发体系。Typesafe 体系包括 Scala、Akka 中间件、Play Web 框架以及管理/监视控制台和开发工具。

Oracle 技术网:您觉得编程过程的哪个方面最有趣?

Bonér:我喜欢编程的所有方面。我认为最有趣的是整个历程,接受一个想法、实现它、使其适合更广泛的情形的创意过程。

Oracle 技术网:关于 Java 平台的发展,最令您感到吃惊的是什么?

“JVM 如此优秀,非常令人兴奋;您对其了解越深,就会感到越惊讶。目前的其他平台远远无法与其媲美。这正是 Java 平台永葆青春的原因。”

Jonas Bonér
Java Champion

Bonér:JVM。JVM 如此优秀,非常令人兴奋;您对其了解越深,就会感到越惊讶。目前的其他平台远远无法与其媲美。这正是 Java 平台永葆青春的原因。

Oracle 技术网:对您来说必不可少的 Java 类是什么?

Bonér:“AtomicReference”— 它为我们提供了编写高性能的无锁并发代码的可能性。

Oracle 技术网:您对 Java EE 7 有何期待?

Bonér:支持尾调用优化和连续。我还喜欢通过 java.util.concurrent API 执行高性能 CAS 的可能性,而不必借助于 sun.misc.Unsafe

Oracle 技术网:在您作为技术推广者的工作中遇到的有关编程的最大误解是什么?

Bonér:是认为制作软件是一个制造过程。编程是一项工程。事实不是这样。它是一门技艺。它是一种艺术形式。我们不需要工程师,我们需要能工巧匠和艺术家。

另请参见