让我们先从个人银行业务领域的一个例子开始,稍微了解一下事件是什么。您将从一个模型开始,该模型中所有的函数调用都是同步的、阻塞和执行是完全连续的。您将研究该模型的缺点,并尝试通过引入事件驱动的体系结构来提高响应能力。

考虑下面的功能,您作为一个客户,要求从银行获得所有所持资产的投资组合清单。以下是生成清单所需要获取、计算和聚合的一些典型的行项:

  • 持有的通用货币

  • 股份

  • 贷款信息

  • 退休基金估值

这些项目的一个示例实现可以有如下清单所示的结构

如清单1.13所示,它是一个连续的代码,所有被执行的代码都会阻塞执行的主线程。只有当序列中的前一个函数完成并使其结果在执行的主线程中可用时,才执行下一个函数。结果是,计算的总延迟是所有单个函数的延迟之和,如图1.12所示。

这种情况可能会损害响应能力,因为某些功能可能正在访问数据库或其他基础设施,而这些功能可能需要相当长的时间才能响应。作为一名用户,您不会想要盯着屏幕,因为后端基础设施需要大量的连续计算。正如你很快会看到的,事件为这一令人不安的体验提供了一个缓解的机会。前面的代码无法满足它的承诺的另一方面原因是,它的结构硬连接到本地执行模型(作者在这里有注释:当执行模型是连续的和阻塞的,就像在本例中一样,您可以进行远程过程调用,并向用户呈现本地执行模型的facade。但是,这种方法从来没有扩展过,而且被发现是分布式计算模型谬误的受害者。详情参考Arnon Rotem-Gal-Oz 写的《Fallacies of Distributed Computing Explained》)。当您有网络通信时候,并且你的投资组合的各种组件需要从从一个使用多个服务而不是单个机器的计算机集群中获取,那么这就会很麻烦了。这是另一个基于事件的模型地址作为解决方案的领域。

让我们把前面的代码翻出来,以一种将处理分布在多个并行计算单元的方式组织起来,保持主线程作为一个协调器的角色(请参阅下面的清单)。在这里,每个单独的函数不再保证在将控制权返回给主线程之前,它将返回一个Balance。相反,它返回一个future,它是计算的占位符。一个future承诺的是当函数的计算完成时,它最终会给你一个Balance.

但作为一种直接的效果,它不会阻塞主线程。主线程可以继续执行其他任务,而单独的函数可以使用另一个执行线程来完成它的承诺。最终的结果是,所有单独的函数都在各自的线程中执行,只留下主线程作为结果的一个协调器和聚合器。而这就是调用generatePortfolio所发生的事情-----它会收集所有到达的数据,并计算出投资组合的清单。

如果这是一个昂贵的操作,你也可以将这个计算委托给Future(就像清单1.14中那样)。作为一个机敏的读者,您一定已经认识到,在这个场景中,计算的总延迟是执行单个Balance计算函数所涉及的所有延迟的最大值,再加上计算 generatePortfolio 的延迟。但是,由于您还将整个计算功能委托给Future,因此执行的主线程可以自由地为其他请求提供服务,而不必等待这些函数的完成。注意,您已经获得了响应性的提高(作者注:通过使用事件和异步编程模型讨论了这样的性能改进,但是事实是并不是所有计算都可以通过异步和非阻塞获得受益。cpu密集型的操作通常可以通过阻塞来获益,因为它们可以利用缓存的一致性和缺少调度器的开销。)!

清单1.14中的通过Future进行计算改变了清单1.13中的阻塞,顺序的代码,使其变为了异步和非阻塞的代码。当计算返回Future时,它相当于向调用线程发送一个事件,该线程并向调用线程说“当我完成时,我将使结果可用”。当计算完成时,调用线程会得到一个指示结果可用性的事件。然后,它可以从它先前注册的回调中获取结果。您现在看到的是事件驱动编程的一种形式,这些事件通过Future的抽象被隐式地发送给调用线程。

results matching ""

    No results matching ""