响应式编程

响应式编程是一种异步编程范式,它关注数据流和变化的传播。这意味着可以通过使用编程语言轻松地表示静态(例如数组)和动态(例如事件发射器)数据流。

Reactive Streams为基于JVM的响应库提供了规范,它定义了一组接口和交互规则。在Java 9中,这些接口已经集成到java.util.concurrent.Flow类之下。

在面向对象编程语言中,响应式编程通常以观察者模式的扩展呈现。还可以将响应式流模式和迭代器模式比较,一个主要的区别是,迭代器基于”拉“,而响应式流基于”推“。

使用迭代器是一种命令式编程,由开发者决定何时去访问序列中的next()元素。而在响应式流中,与Iterable-Iterator对应的是Publisher-Subscriber。当新的可用元素出现时,发布者通知订阅者,这种”推“正是响应的关键。此外,应用于推入元素上的操作是声明式的而不是命令式的:程序员要做的是表达计算的逻辑,而不是描述精准的控制流程。

除了推送元素,响应式编程还定义了良好的错误处理和完成通知方式。发布者可以通过调用next()方法推送新的元素给订阅者,也可以通过调用onError()方法发送一个错误信号或者调用onComplete()发送一个完成信号。错误信号和完成信号都会终止序列。

响应式编程非常灵活,它支持没有值、一个值或n个值的用例(包括无限序列,例如时钟的连续滴答声)。

但是让我们首先考虑,为什么我们需要响应式编程?

阻塞可能是浪费的

现代的应用需要满足大量的用户并发访问,尽管硬件的能力依然在不断提高,软件的性能仍然是一个关键问题。

通常有两种方法可以提升程序的性能:

并行化:使用更多的线程和更多的硬件资源。

在如何使用现有资源方面寻求更高的效率。

通常,Java开发者使用阻塞代码编程。在出现性能瓶颈之前,这种做法没有问题,此时就需要引入额外的线程,来运行相似的阻塞代码。但是,这种资源利用率的扩展可能很快引来争用和并发问题。

更糟糕的是,阻塞会浪费资源。如果仔细观察,只要程序涉及一些延迟(特别是IO,比如数据库请求或网络调用),资源就会被浪费,因为一个(或多个线程)正处于空闲状态,等待数据。

所以,并行化方法并不是什么灵丹妙药。为了提高硬件资源的利用率,响应式编程是必要的。但是它也很复杂,因为容易造成资源浪费。

使用异步来解决?

上文提到的第二种方法是寻求更高的效率,可以解决资源浪费问题。通过编写异步非阻塞代码,你可以将执行切换到另一个使用相同底层资源的活动任务上,在异步执行完成后返回到当前程序。

如何在JVM上编写异步代码呢?Java提供了两种异步编程模型:

Callbacks:异步方法没有返回值,但是提供一个额外的回调参数(一个lambda或者匿名类对象),当结果可用时调用该参数。

Futures:异步方法立即返回一个Future对象。异步线程计算一个T值,Future对象封装对它的访问。该值不是立即可用的,但可以轮询Future对象,直到该值可用为止。

这些技术足够好吗?并不是每个场景都适用,而且两种方式都有限制。

回调是很难组合在一起的,很快就会导致难以阅读和维护的代码(称为”回调地狱”)。

Futures比回调稍微好一点,但是在组合方面依然做的不够好,即使Java 8中引入了CompletableFuture。把多个Future编排到一起虽然可行,但是并不容易。而且,Future还有另外的问题:通过调用get()方法,很容易让Future对象进入到另一种阻塞情景;它们不支持延时计算;不支持多值和高级错误处理。

从命令式编程到响应式编程

响应式编程库Reactor旨在解决JVM上这些”经典“的异步编程方式的缺点,同时关注几个额外的方面:

可组合性和可读性;

使用丰富的操作符词汇操作数据流;

在订阅数据流之前什么也不会发生(延时计算);

背压(backpressure)或消费者向生产者发送发射速率过快的信号的能力;

与并发无关的高级抽象;

可组合性和可读性

可组合性指的是编排、协调多个异步任务的能力。包括使用先前任务的结果为后续任务提供输入,或者以fork-join格式执行多个任务以及在更高级别的系统中将异步任务作为独立组件重用。

编排任务的能力与代码的可读性和可维护性是紧密相关的。随着异步处理的数量和复杂性的增加,编写和阅读代码变得越来越困难。正如我们所看到的,回调模式很简单,但是它的一个主要缺点是,对于复杂的任务,你需要从回调中执行回调,回调本身嵌套在另一个回调中,等等。这种混乱被称为回调地狱。可以想象,这样的代码很难回头进行推理。

装配线类比

你可以将在响应式应用中处理数据想象成(数据)在装配线上移动。响应式编程既是传送带,又是工作站。原材料从一个数据源(最初的Publisher)中倾泻而出,最终成为准备推送给消费者(Subscriber)的成品。

原材料可以经过各种转换和中间步骤,或者成为将中间部件组装在一起的大型装配线的一部分。如果在某一点发生了小故障或者阻塞,受影响的工作站可以向上游发信号限制原材料的流动。

操作符

操作符就是装配线中的工作站。每一个操作符都向发布者(Publisher)添加新的行为,并将上一步中的发布者包装到一个新的实例中。整个链条就是这样连接在一起的,数据源自第一个发布者(Publisher),并沿着链条向下移动,在每个链接处被转换。最终,一个订阅者(Subscriber)结束这个流程。

订阅前什么也不会发生

在Reactor中,当你编写一个发布者(Publiser)链时,默认情况下不会开始向其中注入数据。相反,你创建的是一个异步处理的抽象描述(这有助于重用性和组合)。

通过订阅操作(Subscribe()),你将发布者绑定到一个订阅者,从而触发整个链中的数据流。这是通过订阅者(Subscriber)发出一个请求(request())信号在内部实现的,该信号向上游传播,一直到源头发布者(Publisher)。

背压

向上游传播信号也被用来实现背压。在装配线类比中,我们将其描述为当工作站处理速度比上游工作站慢时,沿装配线向上游发送的反馈信号。

响应式流规范中定义的真正的机制与装配线类比非常相似:订阅者可以在无限制的模式下工作,并让数据源以最快的速度推送数据;或者它可以用request()机制向数据源发出信号,表明自己最多可以处理n个数据。

中间操作符还可以在传输过程中更改请求。假设有一个buffer操作符以10个元素为一个批次对元素进行分组。如果订阅者请求1个缓冲区,数据源就要生成10个元素。一些操作符还实现预取(prefetching)策略,这避免了每次请求一个元素的往返开销,如果在被请求之前生成元素的成本不是太高,那么这样做是有益的。

预取策略将推模型转换为推-拉混合模型,在这种混合模型中,下游可以从上游拉出n个元素(如果它们已经可用)。但是,如果元素还没有准备好,它们会在生产好之后由上游推给下游。

热响应式序列和冷响应式序列

在响应式编程库的Rx家族中,我们可以区分两大类响应式序列:热响应式序列和冷响应式序列 。这种区别主要与响应式流如何对订阅者做出响应有关:

冷响应式序列对每个订阅者(包括数据源)会重新启动一个全新的响应序列。

热响应式序列对每个订阅者不会重零开始。相反,后来的订阅者只能接收到他们订阅后发出的数据。但是,请注意,一些热响应式序列可以缓存或回放全部或部分数据发布历史。从一般的角度来看,热序列甚至可以在没有订阅者在监听时发射数据(“订阅前什么都不会发生”规则的一个例外)。

java 响应式编程_响应式编程相关推荐

  1. java函数式编程_说说函数式编程的那些事

    今天这篇文章我们主要来聊聊函数式编程的思想. 函数式编程有用吗? 什么是函数式编程? 函数式编程的优点. 总所周知 JavaScript 是一种拥有很多共享状态的动态语言,慢慢的,代码就会积累足够的复 ...

  2. java面向方面编程_面向方面编程的介绍----基本概念

    面向对象的编程中常用的概念是:继承.封装.多态.在面向方面的编程中常使用的概念是:advices/interceptors, introductions, metadata, and pointcut ...

  3. python最简单的图形编程_图形化编程、Python、Java、C++到底哪个适合你?

    近两年,学习编程的热潮一波接一波,编程语言也是五花八门.盒子姐姐找到了一份2019年编程语言热度排行榜,其中,Java.C语言和Python高居榜首. 有些小伙伴就要问啦,编程语言这么多,区别在哪里呢 ...

  4. libevent c++高并发网络编程_高并发编程学习(2)——线程通信详解

    前序文章 高并发编程学习(1)--并发基础 - https://www.wmyskxz.com/2019/11/26/gao-bing-fa-bian-cheng-xue-xi-1-bing-fa-j ...

  5. 函数式编程和面向对象式编程_比较函数式编程,命令式编程和面向对象的编程

    函数式编程和面向对象式编程 As Oracle Corporation has introduced some Functional constructs in Java SE 8, now-a-da ...

  6. abb机器人半圆编程_机器人示教编程1——教你快速示教ABB机器人

    一.机器人安全操作守则 由于机器人系统复杂而且危险性大,在练习期间,对机器人进行任何操作都必须注意安全.无论什么时候进入机器人工作范围都可能导致严重的伤害,只有经过培训认证的人员才可以进入该区域. 以 ...

  7. python编程狮的在线编程_‎「Python编程狮-零基础学Python」をApp Storeで

    Python编程狮是W3Cschool编程狮旗下专门为零基础Python编程爱好者打造的一款入门工具App,致力于帮助初学者入门,轻松迈入编程世界.学Python,从这里开始! [零基础也能学]初学者 ...

  8. 常州儿童学机器人编程_常州幼儿编程机器人

    常州幼儿编程机器人 来源:教育联展网    编辑:佚名    发布时间:2019-07-08 少儿编程的发展前景 18年4月28日,全球首部人工智能普教教材--<人工智能基础>(高中版)在 ...

  9. 在有赞做java有发展吗_响应式架构与 RxJava 在有赞零售的实践

    随着有赞零售业务的快速发展,系统和业务复杂度也在不断提升.如何解决系统服务化后,多个系统之间的耦合,提升业务的响应时间与吞吐量,有效保证系统的健壮性和稳定性,是我们面临的主要问题.结合目前技术体系和业 ...

  10. java契约式编程_契约式设计 Design by contract

    在我职业之初,我想得最多的是:当调用一个方法时,传入的参数,我是进入该方法前检查呢?还是进入到该方法后检查?特别是在某个方法中,为了保证该方法能顺利执行,之前的条件检查.if-else-简直是噩梦.所 ...

最新文章

  1. 为什么数据挖掘很难成功?
  2. C++ - const 与 迭代器(iterator) 使用 详解
  3. Linux字符驱动中动态分配设备号与动态生成设备节点
  4. 一点一点学ASP.NET之基础概念——HttpHandler
  5. MobileNet(v1、v2)——CNN经典网络模型详解(pytorch实现)
  6. 理解R-CNN、SPP-NET、Fast R-CNN、Faster R-CNN、FPN博文整理
  7. ResourceBundle 读取properties文件中文乱码
  8. Web报表工具FineReport二次开发JS之字符串
  9. 恒星播放器 for Mac(万能视频播放器)
  10. 深度好文| Redis面试全攻略
  11. 感动世界的50首歌和他们背后的故事3
  12. API监控平台,统一监控系统API
  13. Selenium WebDriver(1)——入门篇
  14. strcmp和==比较
  15. TypeScript 开发环境的搭建与数据类型
  16. airpods二代降噪吗_华强北 苹果二代三代 蓝牙耳机airpods。不跳电,真降噪。
  17. 解决typescript 提示 Object is possibly ‘null‘
  18. windows 设备管理器中的设备控制
  19. 企业级虚拟化实战之KVM——从KVM到云计算OpenStack
  20. 解决2K、4K等高分屏下Photoshop窗口、字体太小等问题

热门文章

  1. android下拉菜单_如何调整和重新排列Android的快速设置下拉菜单
  2. Oracle中表pagesize,Oracle使用pagesize命令
  3. uni-app小程序如何自定义标题内容(如何解决小程序标题不居中)
  4. 微信公众号迁移流程 《openid转换》
  5. 让优秀成为一种习惯——笔录
  6. phpStorm和git解决冲突
  7. VTK:线宽用法实战
  8. 计算机专业论文推荐,计算机专业论文参考文献推荐
  9. Xiaojie雷达之路---脉冲压缩
  10. 使用cache tier