Last Updated:

未来的应用程序会是什么样的?

银狐

16年前,计算机刚刚兴起,简单的HTML代码和并不复杂的后台服务应用程序,就大大改变了新的创新——互联网的兴起。如今,计算机应用程序可以解决问题的场景大大超越了以往任何时候。未来,将会比以往任何时候都成几何倍数地增加。那么如何构建更新颖的应用程序来应对未来各种复杂场景的变化。

Lightbend CTO兼联合创始人乔纳斯·鲍纳给未来应用程序定义蓝图——反应宣言(reactive manifesto 1.0),

以下是该宣言的google翻译:

为未来的应用程序定义蓝图,这些应用程序是事件驱动的,可伸缩的,弹性的和交互式的。

2013年8月22日

注意:本文档是我在2013年中期撰写的第一版Reactive Manifesto。可以在reactivemanifesto.org上找到最新且大大改进的版本。

需要反应

近年来,应用要求发生了巨大变化。仅在几年前,大型应用程序具有数十台服务器,响应时间为数秒,离线维护时间为数十亿字节。今天,应用程序部署在从移动设备到运行数千个多核处理器的基于云的集群的各个方面。用户期望毫秒响应时间和100%正常运行时间。数据需求正在扩展到PB级。

最初是像谷歌或Twitter这样的创新互联网驱动型公司的领域,这些应用程序特征在大多数行业中都呈现出来。金融和电信是第一个采用新做法来满足新要求的人,而其他人则遵循这些要求。

新要求需要新技术。以前的解决方案强调托管服务器和容器。通过购买更大的服务器和通过多线程进行并发处理来实现扩展。通过复杂,低效且昂贵的专有解决方案添加了额外的服务器。

但是现在,一种新的架构已经发展到允许开发人员构思和构建满足当今需求的应用程序。我们称这些反应性应用程序。该体系结构允许开发人员构建事件驱动可伸缩弹性交互的系统:以可扩展且具有弹性的应用程序堆栈为后盾,提供具有实时感的高度交互式用户体验,随时可部署在多核和云计算体系结构上。“反应性宣言”描述了进行反应所需的这些关键特征。

反应性应用

Merriam-Webster将反应定义为“对刺激很容易响应”,即其组件“活跃”并随时准备接收事件。该定义捕获了被动应用程序的本质,重点关注以下系统:

  • 对事件做出反应:事件驱动的性质可以实现以下品质
  • 对负载做出反应:专注于可扩展性而非单用户性能
  • 对失败做出反应:建立具有各级恢复能力的弹性系统
  • 对用户做出反应:结合上述特征以获得交互式用户体验

这些中的每一个都是反应性应用的基本特征。虽然它们之间存在依赖关系,但这些特征与标准分层应用程序架构意义上的层不同。相反,他们描述了适用于整个技术堆栈的设计属性。

反应性特征

在下文中,我们将深入研究这四种品质中的每一种,并了解它们之间的相互关系。

事件驱动

为何重要

基于异步通信的应用程序实现松耦合设计,比仅基于同步方法调用的应用程序更好。可以在不考虑事件如何传播的细节的情况下实现发送者和接收者,从而允许接口关注于通信的内容。这导致实施更容易扩展,发展和维护,为您提供更大的灵活性并降低维护成本。

由于异步通信的接收方可以在事件发生或接收到消息之前保持休眠状态,因此事件驱动方法可以有效利用现有资源,允许大量接收方共享单个硬件线程。因此,与基于阻塞同步和通信原语的传统应用程序相比,处于高负载下的非阻塞应用程序可以具有更低的延迟更高的吞吐量。这样可以降低运营成本,提高利用率并使最终用户更加满意。

关键构建块

在事件驱动的应用程序中,组件通过事件的生成和消费彼此交互 – 分离描述事实的信息。这些事件以异步和非阻塞方式发送和接收。事件驱动的系统往往依赖于推送而不是拉动轮询,即它们在可用时将数据送给消费者,而不是通过让消费者不断地要求或等待数据来浪费资源。

  • 事件的异步发送 – 也称为消息传递 – 意味着应用程序通过设计高度并发,并且可以在不进行更改的情况下使用多核硬件。CPU中的任何核心都能够处理任何消息事件,从而导致并行化机会的急剧增加。
  • 非阻塞意味着应用程序在硬件利用方面非常有效,因为非活动组件被挂起并且其资源被释放,以供其他组件使用。

传统的服务器端体系结构依赖于单个线程上的共享可变状态和阻塞操作。两者都有助于在扩展这样的系统以满足不断变化的需求时遇到的困难。共享可变状态需要同步,这会引入偶然的复杂性和非确定性,使得程序代码难以理解和维护。通过阻塞使线程休眠会占用有限的资源并导致高的唤醒成本。

事件生成和处理的分离允许运行时平台处理同步细节以及如何跨线程调度事件,同时将编程抽象提升到业务工作流的级别。您可以考虑事件如何通过系统传播以及组件如何交互而不是摆弄线程和锁等低级基元。

事件驱动系统实现了组件和子系统之间的松散耦合。正如我们将看到的,这种间接性是可伸缩性和弹性的先决条件之一。通过删除组件之间复杂且强大的依赖关系,可以扩展事件驱动的应用程序,而对现有应用程序的影响最小。

当应用程序受到高性能和大可扩展性要求的压力时,很难预测会出现瓶颈的地方。因此,整个解决方案是异步和非阻塞非常重要。在一个典型的例子中,这意味着设计需要从UI中的用户请求(在浏览器,REST客户端或其他地方)到Web层中的请求解析和调度,以及中间件中的服务组件的事件驱动。 ,通过缓存和下载到数据库。如果其中一个层没有参与 – 阻止对数据库的调用,依赖共享的可变状态,调用昂贵的同步操作 – 那么整个流水线停顿和用户将受到延迟增加和可扩展性降低的影响。

应用程序必须从上到下进行反应

Amdahl定律很好地说明了消除链条中最薄弱环节的必要性,根据维基百科的解释如下:

“在并行计算中使用多个处理器的程序的加速受到程序的连续分数的限制。例如,如果95%的程序可以并行化,那么无论使用多少个处理器,使用并行计算的理论最大加速比如图所示为20倍。

阿姆达尔定律

可扩展

为何重要

Merriam-Webster将可扩展”一词定义为“能够轻松扩展或按需升级”。可伸缩的应用程序可以根据其用途进行扩展。这可以通过为应用程序添加弹性来实现,可以根据需要扩展或扩展(添加或删除节点)。此外,该体系结构可以轻松扩展或缩小(在具有更多或更少CPU的节点上部署),而无需重新设计或重写应用程序。弹性使得可以最大限度地降低在云计算环境中运行应用程序的成本,使您可以从其虚拟化的“按使用量付费”模型中获利。

可扩展性还有助于管理风险:提供太少的硬件来跟上用户负载导致不满和客户流失,拥有太多的硬件和操作人员 – 没有充分理由的空闲导致不必要的费用。可扩展的解决方案还可以降低最终无法使用新硬件的应用程序的风险:我们将看到在未来十年内拥有数百个(如果不是数千个)硬件线程并利用其潜力的处理器需要应用程序可以在非常细粒度的水平上进行扩展。

关键构建块

基于异步消息传递的事件驱动系统为可伸缩性提供了基础。它实现了组件和子系统之间的松散耦合,从而可以将系统扩展到多个节点,同时保留具有相同语义的相同编程模型。添加组件的更多实例会增加系统处理事件的能力。在实现方面,通过利用多个核来扩展或通过利用数据中心或集群中的更多节点来扩展之间没有区别。应用程序的拓扑结构成为部署决策,通过响应应用程序使用的配置和/或自适应运行时算法来表示。这就是我们所说的位置透明度。

重要的是要理解,目标不是试图实现透明的分布式计算,分布式对象或RPC式通信 – 这已经尝试过,而且已经失败了。相反,我们需要拥抱网络通过异步消息传递直接在编程模型中表示它。真正的可扩展性自然涉及分布式计算和节点间通信,这意味着遍历网络,正如我们所知,这本身就是不可靠的。因此,重要的是在编程模型中明确网络编程的约束,权衡和失败场景,而不是将它们隐藏在试图“简化”事物的漏洞抽象之后。因此,同样重要的是提供封装公共构建块的编程工具,以解决在分布式环境类机制中出现的典型问题,以实现提供更高程度可靠性的共识或消息传递抽象。

弹性

为何重要

应用程序停机时间是业务可能发生的最具破坏性的问题之一。通常的含义是操作只是停止,在收入流中留下一个漏洞。从长远来看,它还可能导致不满意的客户和不良的声誉,这将对业务造成更严重的伤害。令人惊讶的是,应用程序弹性是一项很大程度上被忽略或使用ad-hoc技术进行改进的要求。这通常意味着使用过于粗糙的工具以错误的粒度级别处理它。一种常见技术使用应用程序服务器群集来从运行时和服务器故障中恢复。不幸的是,服务器故障转移成本极高且也很危险 – 可能导致整个集群中的级联故障。

Merriam-Webster将弹性定义为:

  • “物质或物体弹回形状的能力”
  • “从困难中迅速恢复的能力”

在反应性应用中,弹性不是事后的想法,而是从一开始就是设计的一部分。使失败成为编程模型中的第一类构造提供了对其作出反应和管理的手段,这导致应用程序通过能够在运行时自我修复和修复来高度容忍失败。传统的故障处理无法实现这一点,因为它在小型中具有防御性,在大型中具有过于激进性 – 您可以在发生异常的地方和时间处理异常,也可以启动整个应用程序实例的故障转移。

关键构建块

为了管理失败,我们需要一种方法来隔离它,以便它不会传播到其他健康的组件,并观察它,以便可以从失败的上下文之外的安全点进行管理。想到的一种模式是图片所示的舱壁图案,其中系统是从安全隔间构建的,这样如果其中一个失败,其他的则不受影响。这可以防止级联故障的典型问题,并允许单独管理问题。

舱壁

实现可伸缩性的事件驱动模型还具有实现此故障管理模型的必要原语。事件驱动模型中的松耦合提供了完全隔离的组件,其中故障可以与其上下文一起捕获,封装为消息,并发送到可以检查错误并决定如何响应的其他组件。

这种方法创建了一个系统,其中业务逻辑保持干净,与意外处理分开,其中故障被明确建模,以便以声明方式划分,观察,管理和配置,并且系统可以自我修复并自动恢复。如果隔间以分层方式构造,它最有效,就像一个大型公司,其中问题向上升级,直到达到有能力处理它的水平。

这个模型的优点在于它纯粹是事件驱动的,基于被动组件和异步事件,因此位置透明。实际上,这意味着它在分布式环境中工作,其语义与本地上下文相同。

互动

为何重要

交互式应用程序是实时,引人入胜,丰富和协作的。企业通过互动体验欢迎他们,与客户建立开放和持续的对话。这使它们更有效率,创造了连接和装备的感觉,以解决问题和完成任务。一个例子是Google Docs,它使用户能够实时协作地编辑文档 – 允许他们在制作时实时查看彼此的编辑和评论。

当用户可以与实时转换为有意义信息的数据进行交互时,用户可以获得授权。交互式应用程序使每个界面中固有的信息协作,从而使人们更有效,更频繁地进行交流; 通过将反馈粒度从传统的整页行为增加到每个项目级别(例如,在单页电子邮件Web客户端中),可以进一步加深这一点。远距离的即时社交互动从根本上改变了人们如何与信息互动。例如,GitHub通过基于浏览器的交互式应用程序通过“社交编码”彻底改变开发者协作,当然Twitter已经深刻地改变了新闻的传播方式。

在事件驱动的基础上构建,响应式应用程序具备良好的交互性。当应用程序变得流行时,可伸缩性是保留此属性所必需的,并且弹性可确保其用户不断享受其功能。

关键构建块

反应式应用程序使用可观察模型,事件流和有状态客户端。

可观察模型使其他组件能够在状态发生变化时接收事件。这可以提供用户和系统之间的实时连接。例如,当多个用户同时在同一数据集上工作时,更改可以在它们之间进行双向同步。

事件流构成了构建此连接的基本抽象。保持它们的反应意味着避免阻塞,而是允许异步和非阻塞转换。例如,可以通过实时投影传递实时数据流,该实时投影产生新的分析数据流。

大多数被动应用程序都拥有丰富的Web和移动客户端,可以创建引人入胜 这些应用程序在客户端执行逻辑和存储状态,其中可观察模型提供了一种在数据更改时实时更新用户界面的机制。WebSockets或Server-Sent Events等技术使用户界面可以直接与事件流连接,因此事件驱动的系统从后端一直延伸到客户端。这允许响应式应用程序通过使用异步和非阻塞数据传输以可伸缩和弹性方式将事件推送到浏览器和移动应用程序。

考虑到这一点,很明显如何将事件驱动可扩展弹性交互的四种特性相互联系,形成一个有凝聚力的整体:

反应性特征已连接

结论

反应式应用程序代表了解决软件开发中广泛的当代挑战的平衡方法。上构建事件驱动的,基于消息的基础上,他们提供需要确保这些工具的可扩展性弹性。除此之外,它们还支持丰富的实时用户交互。我们预计,未来几年,迅速增加的系统将遵循这一蓝图。