1分钟了解响应式编程 | 基本概念

作者:Mars酱

声明:本文章由Mars酱编写,部分内容来源于网络,如有疑问请联系本人。

转载:欢迎转载,转载前先请联系我!

前言

因为最近AI很火,所以跟着火起来的还有"响应式编程",而有些地方叫"流式编程",我系统是Dubbo的微服务架构,在对接AI的时候遇到了一些问题,所以,借着对接的机会也就简单的学习下这个东西,顺便做个笔记记录。那么,我们先从概念入手吧,一分钟快速了解这些玩意。

什么是响应式编程

搜索了一下,给出了下面的定义:

响应式编程(Reactive Programming,简称 RP)是一种 以"数据流"和"变化传播"为核心的编程范式------简单说,就是把程序中的"数据变化"或"事件"封装成可观察的"流"(Stream),通过声明式的方式定义"数据流的处理逻辑",让系统自动响应数据流中的每一个元素(如事件、数据项、状态变更),并高效处理异步、并发场景。

它的核心目标是:优雅处理异步非阻塞操作、简化并发代码、应对高吞吐/低延迟场景(比如实时数据推送、高并发接口、流式数据处理)

那么,这不就是 和 Dubbo 等分布式框架的核心价值是一致的吗?给的这个概念很难懂,我东搜西搜,自己总结了一下

举个生活中的例子

我是这么理解响应式过程的:

我在瑞幸点了一杯9.9的美式加浓(等于触发"订单事件")→ 最近的门店的服务员接收订单(订阅事件)→ 咖啡吧台制作咖啡(处理数据流)→ 咖啡做好后通知我取餐(推送结果)→ 我取餐(消费结果) → 完成

在这个过程中,我并不需要在吧台点餐,然后盯着服务员制作,做完之后再喊我取餐,整个这个过程是无阻塞的,当我的咖啡做好后会"主动响应"我 ------ 这就是响应式的核心:事件驱动、被动响应、非阻塞等待

其实,它就是个异步的过程,对应到我们的编程中,基本可概括为 3 点:

  1. 数据流:所有"变化"(如:排队几个人、剩余多少倍、等待要多久等等事件情况)都被封装成"可观察的流"(Observable Stream),流中可以包含 0 个、1 个或多个元素(数据/事件)
  2. 变化自动化:当数据流产生新的变化(如:从剩余3杯到 给我制作的结果、从制作完成到等待取餐的结果),系统会自动将变化传递到整个处理链路,无需手动轮询、阻塞等待
  3. 声明式:开发者只需关心如何处理数据流,而不用关心"数据何时到达"、"如何异步等待",这些都会由底层框架自行处理线程调度、异步逻辑

响应式编程那不就是异步编程?

看完上面那个例子,很多人会觉得:这不就是异步编程?搞个MQ队列不就能实现?复杂点的话自己写个异步线程不就行了?额....

响应式编程并非单纯的"异步编程",而是一套标准化的规范

又搜了一下,主要是具备 4 个关键特性,也是它能应对高并发场景的核心:

特性 含义
响应式(Responsive) 系统能快速响应请求/变化(比如数据流到达后立即处理),保证低延迟。
弹性(Resilient) 系统在高负载、部分组件故障时仍能正常响应(比如通过背压控制流量、故障隔离)。
弹性(Elastic) 系统能根据负载自动伸缩(比如数据流激增时动态调整处理资源)。
消息驱动(Message-Driven) 组件间通过异步消息(数据流)通信,避免直接耦合,支持非阻塞交互。

(以上特性参考 Reactive Manifesto )其中最关键的两个技术特性是:

1. 非阻塞

非租塞这个概念应该不用我介绍了,概念可以看我之前的文章 《Java | 一分钟掌握异步编程 | 1 - 基本概念》 这篇。

传统同步编程中,调用一个操作,比如 Dubbo 服务调用、数据库查询,会阻塞当前线程,直到结果返回, ---这会导致线程资源浪费,高并发下其实是容易出现线程耗尽。

响应式编程中,耗时操作会立即返回一个"流对象"(如 Flux/Flowable),当前线程可以继续处理其他任务;当结果产生时,流会通过回调通知线程处理结果------全程无阻塞,线程利用率大幅提升。

示例对比:

  • 同步编程(阻塞式):
java 复制代码
// 调用后阻塞线程,直到结果返回
String result = marsDubboService.getData(); 
System.out.println(result); // 必须等上面执行完才会执行
  • 响应式编程(非阻塞式):
java 复制代码
// 调用后立即返回 Flux,不阻塞线程
Flux<String> resultFlux = marsReactiveDubboService.streamData(); 
// 声明"结果到达后如何处理",当前线程可继续做其他事
resultFlux.subscribe(source -> System.out.println(source)); 

// 或者
Flowable<String> resultFlowable = marsReactiveDubboService.streamData(); 
resultFlowable.subscribe(source -> System.out.println(source)); 

2. 背压

背压在响应式编程中是很重要的概念。当"数据流"生产者的速度超过"消费者"处理数据的速度时,会导致数据堆积、内存溢出,而背压就是解决这个问题的机制:消费者可以主动告诉生产者"我能处理多少数据",生产者根据消费者的处理能力调整数据发送速度

比如 Flowable(RxJava)、Flux(WebFlux)都原生支持背压,它们会:

  • 消费者处理能力弱时,会通知生产者"暂停发送",直到自己处理完缓存数据;
  • 消费者处理完后,再通知生产者"继续发送",避免数据溢出。

响应式编程 vs 传统异步编程

传统异步编程(如 Java 的 RunnableFuture)存在明显痛点,而响应式编程正是为解决这些痛点而生:

1. 传统异步编程的问题:回调地狱

当多个异步操作存在依赖关系时(如"先调用 Dubbo 服务 A,再用 A 的结果调用服务 B,再用 B 的结果调用服务 C"),会出现嵌套多层回调的代码,可读性、可维护性极差,这就是回调地狱,代码如下:

java 复制代码
// 传统 Future 异步回调
marsDubboServiceA.getData().addCallback(a -> {
    marsDubboServiceB.process(a).addCallback(b -> {
        marsDubboServiceC.save(b).addCallback(c -> {
            System.out.println("最终结果:" + c);
        }, error -> error.printStackTrace());
    }, error -> error.printStackTrace());
}, error -> error.printStackTrace());

2. 响应式编程的解决:链式调用解决地狱问题

响应式框架提供了丰富的"操作符"(如 mapflatMapfilter),可以将多个异步操作串联成一条"处理流水线",能够解决回调地狱:

less 复制代码
// 响应式链式调用
marsDubboServiceA.reactiveGetData() // 返回 Mono<String>
.flatMap(a -> marsDubboServiceB.reactiveProcess(a)) // 依赖 A 的结果调用 B
.flatMap(b -> marsDubboServiceC.reactiveSave(b)) // 依赖 B 的结果调用 C
.subscribe(
    c -> System.out.println("最终结果:" + c),
    error -> error.printStackTrace()
);

常见响应式框架

既然能解决地狱问题,那么响应式编程有哪些框架支持呢,以下是 Java 生态中最常用的框架:

  1. RxJava :最早成熟的响应式框架,支持 Flowable(背压)、Observable(无背压)、Single(单结果)等,适合需要精细控制数据流的场景
  2. Spring WebFlux :Spring 生态的响应式 Web 框架,基于 Reactor 实现,提供 Flux(多结果流)、Mono(单结果流),与 Spring 生态(如 Spring Boot、Spring Cloud)无缝集成,是 Dubbo 响应式调用的首选
  3. Reactor :Spring WebFlux 的底层依赖,是 JDK 9+ Flow API 的实现,专为高并发、低延迟设计,性能优秀

响应式编程的核心组件

不管是 RxJava、Spring WebFlux 还是其他响应式框架,都遵循一套通用的组件模型,对应"生产者-消费者"的交互流程:

组件 作用 对应实例(框架)
发布者**(Publisher)** 数据/事件的生产者,负责生成并发送数据流(可发送 0~N 个元素)。 RxJava 的 Flowable/Observable、WebFlux 的 Flux/Mono
订阅者**(Subscriber)** 数据/事件的消费者,订阅发布者的数据流,接收并处理数据。 RxJava 的 Subscriber、WebFlux 的 Subscriber
订阅**(Subscription)** 连接发布者和订阅者的桥梁,订阅者通过它请求数据、取消订阅(背压核心)。 RxJava 的 Subscription、JDK 9+ 的 Flow.Subscription
处理器**(Processor)** 既是发布者也是订阅者,用于转换、过滤、合并数据流(如"将字符串转成整数")。 RxJava 的 Processor、WebFlux 的 Processor

核心流程:

  1. 订阅者通过 subscribe() 订阅发布者;
  2. 发布者创建 Subscription 并返回给订阅者;
  3. 订阅者通过 Subscription 向发布者请求数据(如"请求 10 条数据");
  4. 发布者根据请求发送数据给订阅者(触发 onNext() 回调);
  5. 数据流结束时,发布者触发 onComplete() 回调;
  6. 发生异常时,发布者触发 onError() 回调。

响应式编程的适用场景

响应式编程这么牛逼,它并不是所有场景都适合的,只适合以下场景:

  1. 高并发、低延迟需求:如秒杀系统、实时交易系统、高吞吐接口(Dubbo 响应式调用场景)。
  2. 流式数据处理 :如实时日志分析、物联网设备数据推送、大数据分批传输(对应 Dubbo 的 Flux/Flowable 流式调用)。
  3. 多异步操作依赖:如多个 Dubbo 服务调用、数据库查询存在依赖关系(用链式调用替代回调地狱)。
  4. 资源有限的场景:如微服务网关(需要高效利用线程资源处理大量请求)。

不适合场景:

  • 同步业务:如普通 CRUD 操作,响应式编程的"声明式语法"会增加不必要的复杂度;
  • 强事务依赖场景:响应式编程的异步特性会增加事务管理的难度

总结

概念搜了一大堆,我反正是看懂了,下个章节简单讲讲Dubbo给的官方的响应式编程示例,我工程的架构是用的dubbo,好了,下个一分钟再见。

相关推荐
li.wz9 小时前
Spring Bean 生命周期解析
java·后端·spring
sanggou9 小时前
【实战总结】Spring Boot 后端接口防抖详解与实现方案(含注解 + Redis)
spring boot·后端
czlczl200209259 小时前
深入解析 ThreadLocal:架构演进、内存泄漏与数据一致性分析
java·jvm·架构
Victor3569 小时前
Hibernate(26)什么是Hibernate的透明持久化?
后端
盖世英雄酱581369 小时前
不是所有的this调用会导致事务失效
java·后端
Victor3569 小时前
Hibernate(25)Hibernate的批量操作是什么?
后端
少许极端10 小时前
Redis入门指南(五):从零到分布式缓存-其他类型及Java客户端操作redis
java·redis·分布式·缓存
Thetimezipsby10 小时前
Go(GoLang)语言基础、知识速查
开发语言·后端·golang
为自己_带盐10 小时前
从零开始玩转 Microsoft Agent Framework:我的 MAF 实践之旅-第二篇
后端·microsoft·ai·.net
宠..10 小时前
优化文件结构
java·服务器·开发语言·前端·c++·qt