5 Things to Know About Reactive Programming
反应性,这是一个超载的单词。 如今,许多事情变成了神奇的反应。 在本文中,我们将讨论响应式编程,即围绕异步数据流构建的开发模型。
我知道您不急于编写您的第一个反应式应用程序,但是在这样做之前,需要了解几件事。 使用反应式编程会改变您设计和编写代码的方式。 在上火车之前,最好先知道要去的地方。
在这篇文章中,我们将解释有关反应式编程的五件事,以了解它为您带来的变化。
1.反应式编程是使用异步数据流进行编程
使用反应式编程时,数据流将成为您的应用程序的主干。 事件,消息,呼叫甚至故障将通过数据流传达。 通过反应式编程,您可以观察这些流并在发出值时作出反应。
因此,在您的代码中,您将要创建任何东西的数据流:单击事件,HTTP请求,接收的消息,可用性通知,变量的更改,缓存事件,传感器的度量,可能发生的任何变化或 发生。 这对您的应用程序有一个有趣的副作用:它本来就是异步的。
响应式扩展(http://reactivex.io,又名RX)是响应式编程原理的一种实现,以"通过使用可观察的序列来组成异步和基于事件的程序"。 使用RX,您的代码可以创建并预订名为Observables的数据流。 虽然反应式编程是关于概念的,但RX为您提供了一个了不起的工具箱。 通过结合观察者和迭代者模式以及功能性习语,RX为您提供了超能力。 您拥有组合,合并,过滤,转换和创建数据流的功能库。 下图说明了RX在Java中的用法(使用https://github.com/ReactiveX/RxJava)。
尽管RX并不是反应式编程原理的唯一实现(例如,我们可以引用BaconJS –http://baconjs.github.io),但它是当今最常用的。 在本文的其余部分,我们将使用RxJava。
2.可观察的物体可能是冷的也可能是热的,这很重要
此时,您正在尝试查看要在程序中处理的不同流(或可观察对象)是什么。 但是有两类流:热流和冷流。 理解差异是成功使用反应式编程的关键。
冷的可观察物是懒惰的。 在有人开始观察它们之前(订阅RX),他们什么也不做。 它们仅在消耗时才开始运行。 冷流用于表示异步操作,例如,直到有人对结果感兴趣后才会执行。 另一个示例是文件下载。 如果没有人要对数据做任何事情,它将不会开始提取字节。 冷流产生的数据不会在订阅者之间共享,订阅时您将获得所有项目。
订阅之前,如股票报价器,传感器或用户发送的数据之类的热流都处于活动状态。 数据独立于单个订户。 当观察者订阅一个热门的可观察对象时,它将获得 订阅后发出的流中的所有值。 这些值在所有订户之间共享。 例如,即使没有人订阅温度计,它也会测量并发布当前温度。 当订户注册到流时,它会自动接收下一个度量。
为什么了解流是冷还是热如此重要? 因为它会更改您的代码使用已传送项目的方式。 如果您未订阅热播报,那么您将不会收到数据,并且这些数据会丢失。
3.误用异步叮咬
反应式编程定义中有一个重要的词:异步。 当数据在流中异步发出时,您会收到通知,这意味着独立于主程序流。 通过围绕数据流构造程序,您正在编写异步代码:您编写当流发出新项目时调用的代码。 在这种情况下,线程,阻塞代码和副作用是非常重要的事情。 让我们从副作用开始。
没有副作用的函数仅通过其参数和返回值与程序的其余部分进行交互。 副作用非常有用,并且在许多情况下是不可避免的。 但是他们也有陷阱。 使用反应式编程时,应避免不必要的副作用,并在使用它们时有明确的意图。 因此,拥抱不变性和无副作用的功能。 尽管某些情况是合理的,但滥用副作用会导致雷暴:线程安全。
那是第二个重点:线程。 观察流并在发生有趣的事情时收到通知是很高兴的,但是您绝不能忘记谁在调用您,或更确切地说,是在哪个线程上执行您的函数。 强烈建议您避免在程序中使用太多线程。 依赖多个线程的异步程序成为一个棘手的同步难题,通常以死锁搜寻结束。
这是第三点:永不阻塞。 因为您不拥有调用您的线程,所以必须确保永远不要阻止它。 如果您这样做,可以避免发射其他项目,那么它们将被缓冲直到…缓冲区已满(在这种情况下,可能会产生反压,但这不是本文的主题)。 通过结合使用RX和异步IO,您将拥有编写非阻塞代码所需的一切,如果需要更多内容,请查看Eclipse Vert.x,这是一个促进反应性和异步性的反应性工具箱。 例如,以下代码显示了Vert.x Web客户端及其RX API,用于从服务器检索JSON文档并显示名称:
1 2 3 4 5 |
注意最后一个片段中的订阅方法。 当其中一个处理阶段引发异常时,它将采用第二种方法。 始终捕获异常。 如果您不这样做,您将花费数小时来尝试了解问题所在。
4.保持简单
如您所知,"强大的力量伴随着巨大的责任。" RX提供了许多非常酷的功能,并且很容易向阴暗面倾斜。 Chainingflapmap,retry,debounce和zip使您感觉像个忍者……但是,永远不要忘记优质的代码需要别人读取。
让我们来一些代码...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | manager.getCampaignById(id) .flatMap(campaign -> manager.getCartsForCampaign(campaign) .flatMap(list -> { Single<List<Product>> products = manager.getProducts(campaign); Single<List<UserCommand>> carts = manager.getCarts(campaign); return products.zipWith(carts, (p, c) -> new CampaignModel(campaign, p, c)); }) .flatMap(model -> template .rxRender(rc,"templates/fruits/campaign.thl.html") .map(Buffer::toString)) ) .subscribe( content -> rc.response().end(content), err -> { log.error("Unable to render campaign view", err); getAllCampaigns(rc); } ); |
举一个这样的例子,难道不是吗? 它链接了几个异步操作(flatmap),加入了另一组操作(zip)。响应式编程代码首先需要转变思维。 您会收到异步事件的通知。 然后,API可能很难掌握(仅查看运算符列表)。 不要滥用,写评论,解释或绘制图表(我确定您是ASCII艺术艺术家)。 RX功能强大,滥用或不解释它会使您的同事脾气暴躁。
5.反应式编程!=反应式系统
可能是最令人困惑的部分。 使用反应性编程不会建立反应性系统。 如反应式声明中所定义的,反应式系统是一种构建响应式分布式系统的体系结构样式。 反应系统可以看作是正确完成的分布式系统。 反应系统具有以下四个特性:
响应式:响应式系统需要在合理的时间内处理请求(我让您定义合理的)。
弹性:响应式系统必须在出现故障(崩溃,超时,500个错误……)时保持响应,因此必须针对故障进行设计并进行适当处理。
弹性:反应系统必须在各种负载下保持响应能力。 因此,它必须向上和向下扩展,并能够以最少的资源处理负载。
消息驱动:反应系统中的组件使用异步消息传递进行交互。
尽管反应系统的这些基本原理很简单,但是构建其中之一是很棘手的。 通常,每个节点都需要包含一个异步非阻塞开发模型,一个基于任务的并发模型,并使用非阻塞I / O。 如果您不首先考虑这些要点,那么它将很快成为意大利面条盘子。
响应式编程和响应式扩展提供了一种开发模型来驯服异步野兽。 通过明智地使用它,您的代码将保持可读性和可理解性。 但是,使用反应式编程不会将您的系统转换为反应式系统。 反应系统是下一个级别。
结论
我们终于到了这篇文章的结尾。 如果您想走得更远并且对响应式感兴趣,我建议您看一下Eclipse Vert.x(用于构建响应式和分布式系统的工具包(http://vertx.io)),以及Java minibook中的"响应式微服务"。 可从https://developers.redhat.com/promotions/building-reactive-microservices-in-java/获得。 将Vert.x和反应式扩展功能结合使用,可以释放您的反应式超能力。 您不仅可以使用反应式编程,还可以构建反应式系统,并获得激动人心且不断发展的生态系统。
编码愉快!