当前位置: 首页 > 科技观察

反应式架构是未来

时间:2023-03-17 01:32:02 科技观察

反应式编程模型的价值是什么?它的原理是什么?如何正确使用?本文作者将结合自己的学习和使用经验,分享Reactive的概念、规范、价值观和原则。欢迎同学们一起讨论指正。Reactive和Reactive编程Reactive直接翻译为Reactive、Reactive。乍一看,似乎不太好理解。例如:在Excel中,在单元格C上设置函数Sum(A+B),当你改变单元格A或单元格B的值时,单元格C的值也会同时改变。这种行为是反应性的。在计算机编程领域,Reactive泛指响应式编程。指的是一种面向数据流、传播事件的异步编程范式。先举个例子大家感受下:+event)).subscribe();publisher.onNext(1);//print'receiveevent:1'publisher.onNext(2);//print'receiveevent:2'}代码1上面的示例代码(使用Reactor类库)例如,发布者生成数据流(1,2)并将其传播到OnNext事件。在上面的例子中,lambda响应事件并输出相应的信息。在上面的示例代码中,数据流的生成和lambda的注册/执行在同一个线程中,但也可以在不同的线程中。注意:如果对上面代码的执行逻辑有一些疑惑,可以暂时将lambda理解为回调。ReactiveManifestoForReactive现在你应该有一个大概的感觉了,但是Reactive的价值是什么,设计原则是什么,估计你还是有点模糊。这是ReactiveManifesto要解决的问题。使用Reactive方法构建的系统具有以下特点:响应式(Responsive)只要有可能,系统就会及时响应。即时响应是可用性和有用性的基石,但更重要的是,即时响应意味着可以快速发现问题并高效处理。响应式系统专注于提供快速且一致的响应时间,为一致的服务质量建立可靠的反馈上限。这种一致的行为反过来简化了错误处理,建立了最终用户的信任,并推动了用户与系统的进一步交互。弹性系统在发生故障时保持响应。这不仅适用于高可用性、关键任务系统——任何不具备弹性的系统在发生故障时都会失去即时响应能力。弹性是通过复制、遏制、隔离和委派实现的。故障的传播包含在每个组件中,与其他组件隔离,确保系统某一部分的故障不会危及整个系统,并且可以独立恢复。每个组件的恢复委托给另一个(外部)组件,此外,必要时通过复制来保证高可用性。(因此)组件的客户端不再负责处理组件故障。弹性系统在不断变化的工作负载下保持即时响应。反应式系统可以对输入(负载)速率的变化做出反应,例如通过增加或减少分配给为这些输入(负载)提供服务的资源。这意味着在设计中没有争论点和中央瓶颈,支持组件的分片或复制并在它们之间分配输入(负载)。反应式系统可以通过提供相关的实时性能指标来支持预测性和反应性扩展算法。这些系统在传统硬件和软件平台上实现了具有成本效益的弹性。消息驱动的反应式系统依赖于异步消息传递来确保松散耦合、隔离和位置透明的组件之间的明确界限。该边界还提供了将失败委托为消息的方法。使用显式消息传递、负载管理、弹性和流量控制可以通过调整和监视系统中的消息流队列并在必要时应用背压来实现。使用位置透明的消息传递作为通信方式,可以跨集群或在单个主机内使用相同的组合和语义来管理故障。非阻塞通信允许接收者仅在它们处于活动状态时才消耗资源,从而减少系统开销。注意:上面的描述中有很多专有名词,可能会有点混乱。可以看到相关术语的解释。为什么使用Reactive方式搭建的系统会有以上价值,我会在后面Reactor章节介绍。在ReactiveStream了解了Reactive的概念、特点和价值之后,有没有相关的产品或者框架可以帮助我们构建Reactive系统呢?早前有一些类库(Rxjava1.x、Rx.Net)可用,但是规范不统一,所以后来Netflix、Pivo??tal等公司制定了一套规范来指导大家去实现(这个规范也是受到了启发通过早期产品),这就是ReactiveStream的作用。ReactiveStream是使用非阻塞背压进行异步流数据处理的标准。目前,JVM和JavaScript语言都实现了同一套语义规范;并尝试在涉及序列化和反序列化协议的各种传输协议(TCP、UDP、HTTP和WebSockets)的基础上定义一个用于传输反应式数据流的网络。ReactiveStreams的目的是为具有非阻塞背压的异步流处理提供一个标准。ReactiveStreams解决了遇到意外数据流时,仍然可以在资源消耗可控的情况下保持系统可用性的问题场景。ReactiveStreams的目标是控制异步边界上的流数据交换。例如,当将一段数据传递给另一个线程或线程池时,请确保接收方不会缓冲(缓存)任何数量的数据。而背压(backpressure)是解决这类场景必不可少的功能。ReactiveStreams规范的适用范围本标准仅描述了通过背压进行异步流式数据交换的必要行为和实体,以及最小的接口,例如下面的Publisher和Subscriber。ReactiveStreams只关注流式数据在这些组件之间的传递,并不关注流式数据本身的组装、分割、转换等行为,比如map、zip等操作符。ReactiveStreams规范包括:Publisher产生数据流(可能包含无限数据),Subscribers可以根据需要消费数据。publicinterfacePublisher{publicvoidsubscribe(Subscribers);}SubscriberPublisher创建的元素的接收者。监听指定事件,如OnNext、OnComplete、OnError等。发布者和订阅者之间。订阅者可以使用它来取消数据发送或向发布者请求更多数据。公共接口订阅{publicvoidrequest(longn);publicvoidcancel();}Processor同时具有Publisher和Subscriber特征。代码1中,FluxProcessor既可以发送数据(OnNext)也可以接收数据(doOnNext)。publicinterfaceSubscription{publicvoidrequest(longn);publicvoidcancel();}为什么规范强调使用非阻塞异步方法而不是阻塞同步方法?同步方式一般是通过多线程来提升性能,但是系统能够创建的线程数量是有限的,有很多线程之后会造成线程切换的开销。同步很难进一步提高资源利用率。当同步调用所依赖的系统出现问题时,其自身的稳定性也会受到影响。有很多方法可以实现非阻塞。为什么规范选择上述实现?Thread线程不是很轻量级(与以下实现相比)。线程数量有限,最终可能成为主要瓶颈。有些平台可能不支持多线程。例如:JavaScript。调试和实现都有一定的复杂度。回调多层嵌套的回调比较复杂,很容易形成“圣诞树”(回调地狱)。错误处理更复杂。它多用于具有事件循环架构的语言,比如JavaScript。Future不能逻辑组合各种行为,支持的业务场景有限。错误处理仍然很复杂。ReactiveExtensions(Rx)与Futures非常相似。Future可以认为是返回一个独立的元素,而Rx返回一个可以订阅的Stream。多个平台支持同一组规范。同一个API同时支持异步和同步。错误处理很方便。Coroutineskotlin协程和goroutine在语法层面提供异步支持,比Rx更简洁,但无法形成跨多语言平台的统一规范。我个人认为Reactive的实现原理是回调,kotlin协程的实现原理也是回调。但实现回报的方式不同。一种是通过事件传播,另一种是通过状态机。但是协程编程的易用性明显强于Rx。以后有时间我会写一篇文章介绍kotlin协程的实现原理。有了ReactiveStream的规范,Reactor就会有对应的实现该规范的类库。反应堆就是其中之一。Reactor是一个Java语言的Reactive类库,它符合ReactiveStream规范,用于构建非阻塞应用程序。spring5已经集成,类似的类库有RxJava2、RxJs、JDK9Flow等。阿里内部的Faas系统目前采用Reactor构建整个系统,包括功能应用和各种核心应用(逻辑架构)。根据我们的压测结果,使用Reactive方式搭建的系统确实具备这些特点:Resilient:当函数出现严重超时(rt>=10s)时,函数上游的broker和gateway应用几乎没有影响。响应及时:无论是高并发场景(资源充足)还是普通场景,RT始终如一地执行。另外,原则上我觉得资源利用率和吞吐量会比非反应式应用高。为什么Reactive架构体系会有这些特点?阿里内部的Faas系统主要做了两件事:几乎所有与IO相关的部分都是异步的。例如中间件(HSF、MetaQ等提供异步API)调用。IO线程模型更改。使用更少的(平均CPU核心数)线程来处理所有请求。传统的Java应用IO线程模型参考了Netty中的ReactorIO(workerthreadpool)模型,下面的伪代码(kotlin)进行了简化。//非阻塞读取客户端请求数据(in),执行lambda.inChannel.read(in){workerThreadPool.execute{//阻塞处理业务逻辑(process),读取成功后业务逻辑在工作线程池中执行,同步执行后返回输出给客户端被要求池。//非阻塞读取客户端请求数据(in),执行lambdainChannel.read(in){//IO线程执行业务逻辑(process),然后返回输出(out)给客户端。这就要求业务处理流程必须是非阻塞的。process(in){out->outChannel.write(out){//thislambdaisexecutedwhenthewritingcompletes...}}}如何开始ReactiveProgramming有很多学习和发挥价值的地方在以Reactive方式构建的系统中,但坦率地说,Reactive编程方式目前并没有被很好地接受。特别是当我用Java语言给同学开发的时候,我个人也有同感,因为这与Java面向命令和控制过程的编程思维方式有很大的不同。所以这里举一个Reactor(Java)学习的例子:Reactor基础文档ReactiveStreams规范文档Operator总结了反应式系统有很多优点,但是要搭建一个完整的反应式系统并不容易。不仅仅是语言的不同,还有一些组件不支持非阻塞的调用方式,例如:JDBC。但是一些开源组织正在推动这些技术进行创新,例如:R2DBC。此外,一些组织/个人为了方便构建反应式系统,适配了一些主流技术组件,如reactor-core、reactor-netty、reactor-rabbimq、reactor-kafka等,以方便完整的构建reactor系统。反应系统。当你的系统从底部到顶部,从系统内部到系统外部变得反应式时,这就形成了反应式架构。这种架构有多有价值?未来可期。参考https://www.reactivemanifesto.org/https://www.reactive-streams.org/https://kotlinlang.org/docs/tutorials/coroutines/async-programming.htmlhttps://projectreactor.io/docs/核心/发布/参考/index.html