go reactive宣言

13-10-13 banq
这段时间,软件领域暗流涌动:

中间件江湖新来了一批悍将,在互联网领域把EE打得节节败退。Finagle(Twitter)异步化RPC,Akka(TypeSafe)融入管理思路的Actor模型,RxJava(Netflix)观察模式推送多个结果,Vertx(Eclipse)吸取Nodejs精华。它们身着黑衣,上书大大的"Reactive"。并且结党签署投名状: http://www.reactivemanifesto.org/ ---@寻找一个苹果

为什么需要go reactive?

近年来,应用需求已经发生了巨大的变化,仅在几年前,一个大的应用程序就有数万台服务器,秒级响应时间,小时级别离线维护和千兆字节级别的数据。今天的应用程序部署从移动设备到基于云计算的运行在数千个多核处理器上的集群。用户期望毫秒甚至微秒的响应时间和100%的正常运行时间。数据量打到PB级的需求日益增加。

创新的互联网公司如谷歌或Twitter的应用架构设计已经开始推广在很多行业,金融和电信是率先采取新技术来满足新需求的行业。

新的需求要求新的技术。以前的解决方案都强调了服务器或容器的可管理性。可伸缩性性能提升是通过购买较大的服务器以及多线程并发处理来实现。增加新的服务器也同时引入了新的复杂性,这是低效且昂贵的专有解决方案。

但现在已经有一种新的架构让开发商能满足当今的需求构建新的应用程序,。我们称这些应用是reactive应用(反应的应用)。这种结构允许开发人员构建事件驱动(event-driven),可扩展性,弹性的反应系统:提供高度敏感的实时的用户体验感觉,可伸缩性和弹性的应用程序栈的支持,随时可以部署在多核和云计算架构。

什么是reactive

Merriam-Webster 定义reactive是“随时响应刺激”的反应,也就是说,它的组件是“活跃”,随时准备接收事件。这个定义抓住了reactive本质的用。

聚焦在系统上有如下定义:

1. react to events 对事件立即反应

事件驱动的自然特性使的反应能够立即加载实施,通过避免对共享资源的争用实现可扩展性。

2.react to failure 对失败立即反应

在任何级别都可以建立一个能够实现失败恢复的弹性系统。

3.react to users 对用户立即反应

无论任何负载,值得荣耀的响应时间。

这些特征跨越了标准的分层应用程序体系结构,并不局限于某个分层之中,而是适用于在整个技术堆栈。

8
banq
2013-10-13 08:38
事件驱动:Event-driven

基于异步通信的应用程序实现了松耦合的设计,好过于纯粹基于同步的方法调用。发件人和收件人可以实现调用,不要关心事件是如何传播的细节,通过接口实现通信。这就易于延伸,发展和维护,带来更多的灵活性,并降低了维护成本。

由于异步通信的收件人可以在事件发生时或收到一条消息之前保持休眠状态,事件驱动event-driven的方法可以有效地利用现有资源,让多个收件人可以共享一个硬件线程。相比基于同步的传统应用程序,一个非阻塞的应用程序在重负载下可以拥有更低的延迟和更高的吞吐量,这将导致更低的运营成本,提高了资源利用率以及良好的终端用户体验。

banq
2013-10-13 08:54
在一个事件驱动的应用程序中,组件彼此交互是通过离散事件的生产者和消费者( production/consumption )。这些事件是以异步和非阻塞的方式发送和接收的。事件驱动的系统往往依靠推而不是拉或投票表决,即他们是在有消息时才推送数据给消费者,而不是通过一种浪费资源方式:让消费者不断地轮询或等待数据。

异步发送事件:也被称为消息传递意味着应用程序设计于高并发,能够无需改动利用多核硬件。任何一个CPU内核都是能够处理任何消息事件,这导致并行的机会急剧增加。

非阻塞:意味着应用程序在故障等突发情况下任何时候都能实现响应。而对于这一切所需的资源响应,例如CPU,内存和网络都不会发生垄断。因此,它可以同时兼具更低的延迟,更高的吞吐量和更好的可扩展性

传统的服务器端架构是依赖于在一个单独的线程中共享可变状态和实现阻塞操作。这样的系统在满足不断变化的需求时难以扩展伸缩,共享可变状态需要同步,它引入了附带的复杂性和不确定性,使得程序代码很难理解和维护。把一个线程通过阻断方式使用实际是限制了资源,并带来高昂的唤醒成本。

事件的产生和处理的解耦,能够让运行平台更关注同步细节以及事件是如何跨线程分发的,当程序抽象抬升到业务流程级别,你考虑的是事件如何传播以及如何通过系统组件之间进行交互,而不是摆弄周围的低级初始设备如线程和锁。

事件驱动的系统使组件和子系统之间的松耦合。这是可扩展性和弹性的先决条件之一。通过消除复杂和强大的组件之间的依赖,事件驱动的应用可以通过影响最小的方式扩展现有的应用程序。

banq
2013-10-13 09:29
当应用程序遭遇高性能和可扩展性的要求时,这时很难预测会出现瓶颈的地方。因此,重要的是,将整个解决方案都设计为异步和非阻塞(整个系统都是)。

比如在一个典型应用案例中,用户界面中的用户请求(在浏览器中,REST客户端或其他地方)发送到web层进行解析,然后分发到中间件服务组件,通过它的高速缓存然后下推到数据库。如果这些层中任何一个层都不参与以下操作:阻塞调用数据库,依靠共享的易变状态,使用昂贵的同步操作进行调用,那么整个请求处理响应的流水线就不会停顿,用户就不会将遭受延迟通过增加和减少的可扩展性。

An application must be reactive from top to bottom

一个应用必须从顶层到底部都是reactive的。

Amdahl’s Law:

一个程序中使用多个处理器并行计算的加速比是有限的,每个程序都分为串行与并行部分,降低串行的比重,可提高程序的效率:

[该贴被admin于2013-11-22 20:27修改过]


banq
2013-10-13 09:38
Merriam-Webster 定义Scalable的含义是:能够轻松扩展或升级需求。

一个可扩展的应用程序能够根据其用途扩展。这需要通过向应用程序添加弹性实现,能够扩展是指可以扩展出去也能收缩回来(scale out or in比如添加或删除节点)。此外,该架构使得很容易向上或向下扩展(scale up or down比如部署更多或更少的CPU的节点) ,这些扩展都无需重新设计或重写应用程序。弹性可以在云计算环境中让运行的应用程序成本降到最低,让您从pay-for-what-you-use获利。

扩展性也有助于管理风险:硬件太少会跟不上用户负载进而导致客户的不满和损失,但是投入太多的硬件和操作人员,如果空转就造成不必要的损失。一个可扩展的解决方案也减轻了这种风险,在未来十年内将有成千上万的硬件线程,如何利用这些底层潜力,需要应用程序有一个非常细粒的可扩展性。

banq
2013-10-13 09:48
基于异步消息传递提供了一个事件驱动系统的可扩展性Scalable基础。组件和子系统之间的松耦合和位置独立性使其能够向外扩展到多个节点,同时保持相同的编程模型具有相同的语义系统。添加一个组件的多个实例,提高了系统的容量来处理事件。在实施方面没有任何差别扩大利用多个内核,或利用数据中心或群集多个节点向外扩展。应用程序的拓扑结构变成一个通过配置和/或响应的应用程序使用的自适应运行算法进行部署的决定。这就是我们所说的位置透明性location transparency。

重要的是要明白我们的目标是不要使用分布式对象或RPC样式尝试实施透明的分布式计算,这些已经尝试过并且已经失败。相反,我们需要直接通过编程模型的异步消息传递,直接和网络拥抱。真正的可扩展性,自然涉及分布式计算和与节点间的通信,这意味着穿越网络,这在本质上是不可靠的。因此(我们要直接面对不可靠的因素),最重要的是在制约、权衡和故障情况下的进行网络编程,而不是隐藏网络,尝试以一种错误的“简化”方式编程。在分布式环境中类似消息的抽象机制已经达成共识,因为它提供了更高程度的可靠性。

banq
2013-10-13 10:01
应用程序停机时间是最具破坏性的可能发生的问题之一。通过简单的Stop操作,造成了营收漏洞。从长远来看,它也导致客户不满和声誉不佳,会更严重伤害企业。令人惊讶的是,应用弹性是一个在很大程度上被忽略的技术要求。这意味着,我们在解决问题是使用了错误的工具,因为我们站在一个太粗粒度的水平上解决问题。一种常用技术是使用应用服务器集群进行运行和服务器故障的恢复。不幸的是,服务器故障转移是极其昂贵的,危险可能导致了整个集群的级联故障。原因是,我们是站在一个错误的水平上进行故障管理,只有使用细粒度组件才能提高应变能力。

Merriam-Webster定义Resilient弹性:

1.物质或物体拥有形状上弹回的能力。

2.从困难中恢复的能力。

在一个reactive应用中,应变能力不是一个可有可无的东西,而是从一开始就成为设计的一部分。故障应对在编程模型中构建时就第一面对,这就导致应用程序是高度宽容失败并在运行时能够提供手段愈合修复自己。传统的故障处理是不能做到这一点,因为它是要么防御小,要么过于激进,你必须在系统的一个个地方去处理异常Exception(banq:将你的方法到处加上try{}catch)。

[该贴被banq于2013-10-13 10:04修改过]

banq
2013-10-13 10:47
为了管理失败,我们需要一种方法来隔离它,使它不会扩散到其他健康的部位,并能观察它,从失败的上下文以外的安全点对其进行管理。一种模式是bulkhead pattern,如下图,说明系统是建立了安全车厢,所以如果其中一个出现故障,不影响其他的。这可以防止级联故障的经典的问题,并允许隔离管理问题。

事件驱动的松散耦合提供了组件在失败下可以抓获完全隔离的上下文场景,作为消息封装,发送到其他组件时,在具体编程时可以检查错误比如是否接受到,接受的命令是否可执行等等,并决定如何应对。

这种方法创建了一个能让业务逻辑保持清洁的系统,显式的隔离分开处理Exception的有利于观察,管理和配置,并能让系统自行愈合,并自动恢复。这类似在一个大公司内,将一个问题逐步向上升级,直到达到有权力来处理它的水平。

这个模型的美妙之处在于它是纯粹的事件驱动,它是reactive组件和异步事件,意味着在分布式环境中具有在本地一台服务器范围内相同的语义。


banq
2013-10-13 11:00
Merriam-Webster定义Responsive为:迅速回应或作出适当的反应。

我们使用这个词在一般意义上不应该与响应的网页设计混淆,后者主要是指CSS媒体查询和渐进增强。

响应的应用程序是实时的,耐看的,丰富的和协作。企业与他们的客户建立一个开放的和持续的对话,欢迎他们通过响应的互动体验。这使得它们更有效率,建立连接,并配备解决问题和完成任务的感觉。一个例子是,在使用谷歌Docs 时,几个用户能够编辑文档协作,能实时地让他们看到彼此的编辑和注释的结果。

对事件作出响应的应用程序,需要及时去做(just do it),即使存在故障情况。如果应用程序在长时间不做出回应,称为延迟,这个系统实际是不可用的,因此不能被视为弹性。

当然并非所有应用如武器或医疗监控系统这样对实时要求如此高,但是通常他们在运行一段时间后出现迅速下降的性能,才可以从他们偏离了一点响应限制看出(banq: windows越用越慢),假如是金融交易应用程序可能会失去目前的交易没有及时回应。

banq
2013-10-13 11:14
reactive应用使用可观察模型,事件流和有状态的客户端。

可观察的模型:状态变化时能激活系统接收事件。这可以提供一个用户和系统之间的实时连接。例如,当有多个用户同时操作同一模型,改变能够在它们之间双向同步,这样这个模型就不必通过锁的约束进行共享状态。(参考:http://www.jdon.com/45728)

事件流形成于建立连接后的基本抽象。保持他们的reactive是为了避免阻塞,当然允许异步和非阻塞的转换和通信。

Reactive应用积极拥抱算法顺序,通过使用设计模式和测试确保响应事件在O(1)或 O(log n)时间内返回,根本无需顾及系统负载。

考虑一个web应用程序,它具有丰富的客户端,基于浏览器或移动应用程序创建引人入胜的用户体验。此应用程序将在客户端执行逻辑和存储状态,这里有一个可观察的模型提供了一种机制,当实时数据更改时及时更新用户界面。

WebSockets or Server-Sent Events能让用户界面和后端推送的事件流直接连接,这就允许reactive应用能推送事件到浏览器或移动客户端,这是通过一种异步非堵塞的可扩展弹性的方式。

banq
2013-10-13 15:47
点评:

宣言中提到的传统同步架构是指JavaEE 6或Spring或.NET之类的SOA应用,这些应用虽然将软件进行了多层架构划分:Web层(SpringMVC/JSF)、应用服务器层(EJB或Spring)和持久层(Hibernate/JPA等),每层之间包括层内部调用都是同步的。

所谓同步调用,就是普通的方法调用,比如MVC里面的Action的方法调用一个服务的方法,这个服务的方法里面再调用Hibernate或JPA进行存储,返回结果,这种同步调用每个步骤都是在一个线程内发生,属于同一个流水线,如果其中一个环节发生堵塞等待,致使最后响应被延迟。

而reactive引入或者说异步的引入,将这些层之间调用放在不同线程之间实现,不是顺序执行,而可能是并行执行,见:关于Ebay的架构之一Asynchrony Everywhere

reactive实则是让原来被线程访问的每个对象,不管是业务对象还是实体等等,都能够自己发出自己的线程,而不是被动地加入访问的这个线程顺序中。

reactive架构是DDD以及CQRS的自然代码实现,有了reactive,CQRS能够方便实现,DDD的领域模型自己也能够发出事件,能够真正成为系统中指挥主角,而不是被动地被访问的线程激活。

有了reactive/EDA/Actor模型这些概念,我们只要直接基于组件模型编程,而不必有线程概念,屏蔽了底层线程的编程难度,比如在JdonFramework中有两个组件:模型和组件,模型和组件之间可以互发消息事件,在JF框架或其他reactive框架或语言平台上编程,可以回避线程,但是能编制出高并发高吞吐量高事务的应用,这些完全可以替代传统JEE或Spring或.NET。

如果你相信这样的架构代表未来,那么请去宣言网站投票支持,目前全球已有1800名投票。

banq
2013-10-13 19:49
2013-10-13 09:48 "@banq"的内容
重要的是要明白我们的目标是不要使用分布式对象或RPC样式尝试实施透明的分布式计算,这些已经尝试过并且已经失败。 ...

EJB作为分布式对象实现,是一种同步RPC,其实已经宣告失败,这可能对EJB规范是否有下一个版本的重大打击。

而JavaEE中的消息系统规范JMS反而可能被证明是成功的,其也是SOA的ESB一种实现,但是如果不能让程序员直接面对网络通讯编程,比如一个消息消费者可能在另外一台服务器上,也可能是本地的另外一个线程,反正别管消费者在哪里,你都要面对通讯问题,在别的节点服务器,你面对是基于socket的通讯,而如果在一个容器VM内,你面向是线程通讯问题,当然具体怎么通讯已经有现成的库包,关键是你编制业务代码时,你已经知道生产者和消费者是分离,你有这样一个预想场景,你就会提前设计防止可靠性等问题。

而分布式对象如EJB则告诉你,你别管对象在哪台服务器上,你只要闭眼编程即可,到时会自动给你分布式部署,但是网络本身并不是100%可靠,如果一旦出现问题,很难追查,因为你编程时,根本不让你知道可能会分布,粗粒度,你想追查只能请webshpere或oracle等服务器专家来。就是帮助你解决问题,下一次问题出现在分布式网络哪里谁也没有把握,整个系统的可靠性就大大降低。

这篇宣言还提到,不但要scale out 或in,这是指水平或横向扩展,还需要垂直或纵向扩展Scale up/down。我们以前谈分布式网络,总是狠批垂直扩展,水平扩展通过增加廉价服务器容易低成本,CAP定理和BASE原则都出来了,总是说垂直扩展是购买更高级的服务器,其实垂直扩展也可以是增加CPU核数,现在手机都是4核了,多核的垂直扩展看来也是比较廉价的,如何利用廉价多CPU,这就需要reactive的并发编程了,有可能节省好几台廉价服务器的投资,也是一种节约。

垂直扩展适合于结构化数据,比如在CQRS中对DDD聚合的写入操作(Command);而横向扩展适合非结构化数据。

[该贴被banq于2013-10-14 09:47修改过]

banq
2013-10-14 06:36
2013-10-13 11:14 "@banq"的内容
reactive应用使用可观察模型,事件流和有状态的客户端 ...

reactive特别适合有状态的客户端,而REST是一种状态转移接口,reactive + REST + 有态客户端 是未来一种趋势。

为什么要使用MVC+REST+CQRS架构: http://www.jdon.com/45602

基于任务的UI(Task-Based UI):

http://www.jdon.com/45385

猜你喜欢