V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yuhongtai114514
V2EX  ›  Java

响应式编程确实有点看不懂

  •  
  •   yuhongtai114514 · 2023-10-07 23:38:24 +08:00 · 7674 次点击
    这是一个创建于 404 天前的主题,其中的信息可能已经有所发展或是发生改变。
    因为 spring cloud gateway 里用了 project reactor 所以就去研究了一番,但是越看越模糊,只知道它的一些用法,比如在数据流上设定操作符(如 flatMap),然后当调用 subscribe 时,这一系列操作符就会被执行。但这个和普通的回调有什么区别呢,为啥说它是异步非阻塞的?

    再比如在 spring cloud gateway 里,filter 返回的都是 Mono,当 reactory-netty 收到网络请求时,调用 subscribe ,触发 filter 返回的 Mono 中设定的一系列操作。但是实验了一下,发现 filter 之间也是串行阻塞的:
    [在一个 filter 返回的 Mono 的 flatMap 中写下 Thread.sleep(2000L),发现在该 filter 后面的所有 filter 也被阻塞了。但是 gpt 告诉我 filter 之间可以是并行的,因为用了 project reactor ]

    想问问这是为啥,感觉始终没有正确理解响应式编程,想问问 v 友们~
    44 条回复    2024-01-03 23:25:42 +08:00
    BBCCBB
        1
    BBCCBB  
       2023-10-07 23:50:59 +08:00
    可以先理解下 js 里最开始的 callback hell, 然后再看看 Promise 的出现解决了 js 的什么问题. Promise 可以理解成一个最简单的响应式..

    一定程度上解决了 callback hell, 然后加了一些方便开发的 operator 方法.

    不建议看这个东西.. 协程才是正路.
    b1t
        2
    b1t  
       2023-10-08 00:26:53 +08:00
    我看 Spring Cloud Gateway 的时候跟你的感觉是一模一样的,然后我就放弃了
    b1t
        3
    b1t  
       2023-10-08 00:28:28 +08:00
    op 如果找到响应式编程的学习方法,记得 @一下我(逃
    statumer
        4
    statumer  
       2023-10-08 02:32:16 +08:00
    Project Reactor 这些东西就是 monad 在 Java 里的实现。需要这个东西的根本原因是 Java 没有协程,非得用纯函数这种不自然的形式构造状态机。
    现在没什么学的必要了,如果进展理想的话协程在 Java 世界中会迅速普及。
    btw ,回复一下主楼里的问题,你转述的 gpt 的说法是完全错误的,可以自己找本书看或者看 Project Reactor 的 reference ,第三章很好地阐释了基本思想。
    weijancc
        5
    weijancc  
       2023-10-08 08:08:29 +08:00
    在 Spring Cloud Gateway 上开发的代码看着真恶心
    SilenceLL
        6
    SilenceLL  
       2023-10-08 08:19:40 +08:00
    原来大家都看着难受,说明是真的难受
    iOCZ
        7
    iOCZ  
       2023-10-08 08:34:30 +08:00
    不要过分依赖 GPT ,它撒起谎来还有板有眼的
    chendy
        8
    chendy  
       2023-10-08 08:55:00 +08:00
    可以在业务不复杂,对性能又有一些要求的场景使用
    不能用同步语法写异步就是坐大牢
    iovekkk
        9
    iovekkk  
       2023-10-08 08:55:07 +08:00
    按我的理解,响应式编程的意义就是可以把某一个代码块或者函数当做参数,而这一段代码块或者函数的逻辑,可以放到任何地方去实现
    mgcnrx11
        10
    mgcnrx11  
       2023-10-08 09:09:49 +08:00
    因为 op 把“响应式 Reactive” 和 “异步非阻塞” 的概念 混淆为 它两个是一个意思了

    Project Reactor 的响应式里面,当 subscribe 时,一系列的操作会一个接着一个去执行。但这些操作都是我们自己写的代码,还是需要确保这里自己写的代码是“非阻塞”的才行。否则,就会像你举例的一样,sleep(2000L)后发现阻塞了。
    还有的是,除非显式的调用 publishOn / subscribeOn 等方法呢,否则都是在调用 subscribe 的当前线程上执行的。也就会造成该线程被 sleep 阻塞起来的感觉

    > 但是 gpt 告诉我 filter 之间可以是并行的

    这是错误的
    afeiche
        11
    afeiche  
       2023-10-08 09:13:06 +08:00
    spring 那一套和阻塞线程参合在一起,不太好理解,你可以看看 vert.x ,参考 nodejs 搞的,理解起来会容易一点,我没看你过 project reactor ,估计和 vert.x 差不多,底层应该都是 netty 。
    ccde8259
        12
    ccde8259  
       2023-10-08 09:14:43 +08:00 via iPhone
    想搞响应式编程的话,以生产者-消费者模型为基础,得先入门一下函数式编程,再开始理解值容器也就是 Monad……太难了还是别学了
    des
        13
    des  
       2023-10-08 09:19:33 +08:00 via iPhone
    我还以为只有我一个人是这样呢,看的难受
    msaionyc
        14
    msaionyc  
       2023-10-08 09:25:42 +08:00
    理解起来确实费劲,从声明,订阅( subscribe ),请求,执行,正向->逆向->正向->逆向,最后又到正向执行,太绕了。
    而且也不好 debug
    Helsing
        15
    Helsing  
       2023-10-08 09:34:53 +08:00 via iPhone
    RxJava 和 Reactor 的思想是类似的,推荐一篇写的很好的关于 RxJava 的文章,希望对你有帮助:

    https://juejin.cn/post/6844903670203547656
    srx1982
        16
    srx1982  
       2023-10-08 09:47:49 +08:00
    简单的需求还行,如果需求复杂,还要在不同阶段读取多种数据源,那就麻烦了
    mikasa1024
        17
    mikasa1024  
       2023-10-08 10:08:49 +08:00
    之前基于 srping gateway 开发了一个网关管理系统,感受就是业务很难写,到最后只是连着数据库做了个简单的增删查改,然后和网关路由同步起来

    下次有这种需求,我会选择使用 openresty 或者 apisix ,选择 openresty 用 lua 开发的体验应该也比 gateway 的这个响应式强
    Leviathann
        18
    Leviathann  
       2023-10-08 10:31:19 +08:00
    Brian Goetz: "I think Project Loom is going to kill Reactive Programming"
    GiantHard
        19
    GiantHard  
       2023-10-08 10:52:44 +08:00
    > 比如在数据流上设定操作符(如 flatMap),然后当调用 subscribe 时,这一系列操作符就会被执行。但这个和普通的回调有什么区别呢

    没有区别,这些操作符就是在帮助你设置回调函数

    > 为啥说它是异步非阻塞的

    因为 Operator 的执行线程可以由 Scheduler 决定,所以说 Rx (ReactiveX, 响应式编程 API) 可以实现异步非阻塞

    https://projectreactor.io/docs/core/release/api/reactor/core/scheduler/Scheduler.html

    > 一个 filter 返回的 Mono 的 flatMap 中写下 Thread.sleep(2000L),发现在该 filter 后面的所有 filter 也被阻塞了

    Thread.sleep 是「阻塞的 api 」,你可以试试使用 Rx 的 delay https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#delay-java.time.Duration-

    除了 Thread.sleep 之外,Java 世界中有很多库的 API 都是阻塞的,要在 Rx 世界中流畅使用,得自己找对应的 Rx 实现
    yazinnnn0
        20
    yazinnnn0  
       2023-10-08 11:57:04 +08:00
    https://www.reactivemanifesto.org/zh-CN

    响应式和异步非阻塞没啥关系
    Hugehard
        21
    Hugehard  
       2023-10-08 14:07:04 +08:00
    不只是看不懂,debug 我都搞不懂,目前在看一个项目,全靠 gpt 理解
    yuhongtai114514
        22
    yuhongtai114514  
    OP
       2023-10-08 14:32:48 +08:00 via iPhone
    @BBCCBB 感谢,这就去看看~
    yuhongtai114514
        23
    yuhongtai114514  
    OP
       2023-10-08 14:34:12 +08:00 via iPhone
    @b1t ok ~
    yuhongtai114514
        24
    yuhongtai114514  
    OP
       2023-10-08 14:37:12 +08:00 via iPhone
    @statumer 好的,非常感谢,看来我过于相信 gpt 了(´_ゝ`),直接绕晕
    yuhongtai114514
        25
    yuhongtai114514  
    OP
       2023-10-08 14:45:05 +08:00 via iPhone
    @GiantHard 帮助很大! 我去看看 Rx ,感谢解惑🙏
    yuhongtai114514
        26
    yuhongtai114514  
    OP
       2023-10-08 14:46:44 +08:00 via iPhone
    @yazinnnn0 我之前一直以为这两个强关联🥹
    imaple
        27
    imaple  
       2023-10-08 16:06:50 +08:00
    确实有点难, 而且很难调试, 爆炸
    cloud107202
        28
    cloud107202  
       2023-10-08 16:49:45 +08:00
    unco020511
        29
    unco020511  
       2023-10-08 17:43:55 +08:00
    其实 rxjava 更多使用是在界面程序的,比如移动端应用,主线程不能做耗时操作,经常需要使用 callback,需要频繁切换线程.后台我感觉必要性没有那么大
    vivisidea
        30
    vivisidea  
       2023-10-08 17:53:25 +08:00
    subscribe / subscribeOn 调试起来要疯,我们之前有个项目试点了一下响应式,写的人觉得很爽,其它开发一脸懵,维护很麻烦,后来还是抛弃了
    Huelse
        31
    Huelse  
       2023-10-08 18:43:50 +08:00
    不如学函数式编程,其核心思想要普遍的多。
    rrfeng
        32
    rrfeng  
       2023-10-08 19:16:55 +08:00
    当年写 angular 强行学习了一下 rxjs ,学起来太难,但是会了确实很好用(然而已经忘光了)。

    前提是 js ,java/go 还是协程走起吧。
    ychost
        33
    ychost  
       2023-10-08 21:07:43 +08:00
    首先得理解什么是 IO ,IO 底层如何实现「同步非阻塞」的,然后再来看 flatMap 操作符的用处,如果不用 flatMap 自己该如何处理这些 IO 回调,如果业务里面 IO 少的话没必要上 reactor
    mxT52CRuqR6o5
        34
    mxT52CRuqR6o5  
       2023-10-08 21:12:48 +08:00 via Android
    学过 rxjs 也实际应用过,没觉得有多好用(没有必要的话就不要去用),但如果有复杂的异步场景的话,不用 rx 写起来感觉就是寸步难行
    Dlin
        35
    Dlin  
       2023-10-08 22:50:20 +08:00 via Android
    个人也觉得响应式是一种编程思想。是某个事务满足触发操作时触发的同步回调。每个 filter 只是这次触发中的执行过程中的一部分。说它异步,其实只是这一过程并非是人为有意去调用的。而是系统自己去调用的(subscribe)
    comingnine
        36
    comingnine  
       2023-10-09 01:42:37 +08:00 via Android
    感觉解藕真不好调试
    xarthur
        37
    xarthur  
       2023-10-09 03:18:25 +08:00 via iPhone
    其实你需要去了解一些函数式编程,响应式只是很自然的引用……
    zhuangzhuang1988
        38
    zhuangzhuang1988  
       2023-10-09 06:19:49 +08:00 via Android
    https://www.bennadel.com/blog/4519-replacing-rxjs-with-a-state-machine-in-javascript.htm
    想象成状态机试试
    不过状态机会要写很多代码 rx 帮忙解决了
    ma836323493
        39
    ma836323493  
       2023-10-09 10:22:53 +08:00
    看看郭神 的 第一行代码, 了解这个应该轻松电吧
    f14g
        40
    f14g  
       2023-10-09 10:23:50 +08:00 via Android
    以前也是不理解这个,后来学校教 Verilog 的时候遇到了“线变量”这种东西,当时就理解了“响应式编程”了。
    nothingistrue
        41
    nothingistrue  
       2023-10-09 14:02:59 +08:00   ❤️ 2
    六,响应式编程,对应的编程概念是「流」,现实概念有个跟起非常像的,「流水线」。如果要搞这个,首先要把「流」搞清楚。Java8 的 Stream 可以用来入门。Kettle 、Spring Batch 可以用来练手。你最起码是 kettle 或者 Spring Batch (高级流应用,不能是一个处理器转发然后又回到普通代码去处理了) 用得比较熟悉了,才能去接触响应式编程。另外,你也可以直接从函数式编程、数据流,这些数学理论入手,不过前提是要先清空 C 、C++、Java 等全部常规编程习惯。

    一到五跑题了,留着备用看吧。

    一,同异步跟是否阻塞,不是一词两用,而是有关联的两个概念。

    二,阻塞针对的不是当前过程,而是使用同一资源的多个过程之间的协作方式。通常都是 IO 比计算慢,故常见的阻塞是针对 IO 的阻塞。一个 IO 通道,如果只允许同时最多一个过程使用它,在该过程使用它期间其他过程只能等,那么这就是阻塞的。如果允许同时多个过程使用它,那么这是非阻塞的。

    三,同步异步,针对的是调用者和被调用者之间的协作过程。如果被调用者不做完就不回复,同时调用者还一直等着它,那么这是同步的。如果被调用者没处理完就提前做个特殊回复,同时调用者认可这种特殊回复,那么这是异步调用。如果调用者这收到异步特殊回复之后,又再继续等待这个特殊回复转换成完整回复,那么这是异步之后的回转同步,或者,整体上来说,这还是同步调用。

    四,再往外看调用者、非调用者的宿主程序,这时候同异步跟阻塞有了关联。如果把调用者宿主程序认为资源,调用关系——被调用者的执行行为认为处理过程,那么同步调用是阻塞的,异步调用是非阻塞的。

    五,同步还是异步,技术上由被调用者决定,但业务上由有调用者决定。如果调用者实时关心回复结果,那么它永远要是同步调用,即使被调用者技术上异步,最终也要通过异步转回同步来变成同步。异步编程中,如何异步从来都不是关键,如何异步转会同步,获取去掉不必要的业务同步,才是关键。
    yuhongtai114514
        42
    yuhongtai114514  
    OP
       2023-10-09 19:38:59 +08:00
    @xarthur ok~之前还没正式看过函数式调用,只知道是把函数传来传去
    4kingRAS
        43
    4kingRAS  
       2023-10-11 11:11:14 +08:00
    你真正要看阻不阻塞要看线程 id ,当前代码运行在哪个线程
    hdfg159
        44
    hdfg159  
       316 天前 via iPhone
    rxjava 玩一轮,你就懂了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5921 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 02:00 · PVG 10:00 · LAX 18:00 · JFK 21:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.