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

有一个问题想问问大家, 就是 Netty 是异步 I/O,也就是不会在 I/O 的时候不会阻塞,但是 I/O 本身还是耗时的操作,操作系统还是应该需要耗时处理, 效率的提升到底实在什么地方

  •  
  •   iXingo ·
    iXingo · 2019-11-17 11:27:03 +08:00 · 7617 次点击
    这是一个创建于 1842 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我就是想不通的地方就是, 所以 Netty 高效的地方就是连接和分发吗,真正 I/O 耗时的处理还是需要底层操作系统慢慢处理, 是这样吗
    56 条回复    2019-12-02 19:03:24 +08:00
    araaaa
        1
    araaaa  
       2019-11-17 11:29:57 +08:00 via iPhone
    少量线程处理大量连接
    iXingo
        2
    iXingo  
    OP
       2019-11-17 11:31:46 +08:00
    @araaaa 也就是说主要是先接收大量的连接,然后耗时的操作再慢慢等待处理是吗
    lu5je0
        3
    lu5je0  
       2019-11-17 11:32:26 +08:00 via Android
    netty 是同步 IO
    araaaa
        4
    araaaa  
       2019-11-17 11:37:47 +08:00 via iPhone
    @iXingo 去了解下 io 多路复用,和事件轮询。主要作用就是单条线程可以处理复数连接的数据收发
    Infernalzero
        5
    Infernalzero  
       2019-11-17 11:38:13 +08:00
    并不是异步 IO,同步非阻塞
    araaaa
        6
    araaaa  
       2019-11-17 11:40:42 +08:00 via iPhone
    @iXingo 耗时操作使用业务线程池,和 netty 线程池分离
    iXingo
        7
    iXingo  
    OP
       2019-11-17 12:05:30 +08:00
    @Infernalzero 谢谢指正
    iXingo
        8
    iXingo  
    OP
       2019-11-17 12:05:46 +08:00
    @araaaa 好的,我再研究研究
    sandrew1945
        9
    sandrew1945  
       2019-11-17 12:06:34 +08:00 via iPhone
    0 拷贝
    javapythongo
        10
    javapythongo  
       2019-11-17 12:09:18 +08:00 via iPhone
    没有提高计算能力,只是能接收更多的请求来处理,如果事 cpu 计算为主,效率就很高
    hhx
        11
    hhx  
       2019-11-17 12:11:24 +08:00 via Android   ❤️ 1
    Netty 应该是非阻塞 IO,和异步 IO 不是一回事。首先你要搞清楚所谓用户态 IO 到底是在干什么,其实很简单,就是在内核缓冲区与用户进程空间之间传送数据。举个例子,调用 BufferedReader 的 readLine 方法,该方法试图读取一行,但问题是该行数据很可能没有准备好,那怎么办呢?阻塞式 IO 的做法是阻塞当前进程,进程进入阻塞态,由 OS 负责在数据准备好时唤醒该进程,然后继续执行 IO。而非阻塞 IO 的做法是跳过执行该 IO 操作,执行后续代码,典型的做法是,不断轮训以检查数据是否准备好。有人会说了,那阻塞式 IO 岂不是更好,因为阻塞时没有占用 CPU,而非阻塞花费了 CPU 时间去轮训。我的观点是,阻塞尽管没有占用 CPU,但是整个程序依然是止步不前的,当处理较多用户连接时,很明显非阻塞方式的交互式更好,响应时间更低,吞吐量也更大。另一方面,阻塞式 IO 占用更多的线程,而非阻塞 IO 可以利用 OS 的 epoll 等系统调用,实现单一线程处理多个连接,明显更适合服务器。至于异步 IO,效率可能更高,但实却非常复杂。
    RicardoY
        12
    RicardoY  
       2019-11-17 12:11:39 +08:00 via Android
    IO 不占用 cpu 的计算时间
    RicardoY
        13
    RicardoY  
       2019-11-17 12:19:53 +08:00 via Android
    @hhx 异步 io 也不是很麻烦吧 写的只需要把数据先给出去 等到可以写的时候内核写 读则是给出缓冲区 能读了内核帮你拷贝过来
    liuminghao233
        14
    liuminghao233  
       2019-11-17 12:42:31 +08:00
    高不高效跟你 Netty 没关系
    你用 epoll || kqueue 写出来的东西都是差不多的
    相比于一个线程阻塞 n 个连接或者一个线程一个连接来说,io 复用肯定快啊
    zmxnv123
        15
    zmxnv123  
       2019-11-17 12:45:35 +08:00
    本地 io 是 dma 方式的,不占用 cpu 时间,而网络 io 可以认为是网卡操作的,也不占用 cpu 时间。
    liumyao
        16
    liumyao  
       2019-11-17 12:50:44 +08:00 via Android
    你没搞清楚同步 异步 阻塞 非阻塞
    iXingo
        17
    iXingo  
    OP
       2019-11-17 12:55:16 +08:00
    @hhx 好的,谢谢。还有 Netty 的 IO 线程主要做的什么操作,是不是不能在 IO 线程中做一些阻塞的操作,而是要把这些操作放在额外的业务线程池中。您能详细讲一下这方面的操作吗
    iXingo
        18
    iXingo  
    OP
       2019-11-17 12:55:53 +08:00
    @zmxnv123 Netty 主要做的是网络 IO 还是本地 IO
    iXingo
        19
    iXingo  
    OP
       2019-11-17 12:57:05 +08:00
    @liuminghao233 那您的意思是说其实 Netty 底层也只是调用和封装了操作系统的 select/poll/epoll
    opengps
        20
    opengps  
       2019-11-17 12:59:17 +08:00 via Android
    一楼回答跟链接直接,处理大量链接,java 里 netty,c#里 iocp,能实现返利十万以上链接,然而线程只有几个,十几个这样的比例
    lhx2008
        21
    lhx2008  
       2019-11-17 13:03:25 +08:00 via Android
    其实说实在的,netty 快的原因不是接入网络这边( tomcat 也是同步非阻塞接入)
    而是网络 IO 完之后的全异步的调度,就是通过流水线,异步回调这种方法,这样可以缩短线程的阻塞时间,从而减少线程的数量和切换。特别是后端再作为客户端调用别的 API 的时候。如果你用 tomcat 做一个 HTTP 请求出去,就基本只能阻塞线程慢慢等了。
    lhx2008
        22
    lhx2008  
       2019-11-17 13:07:59 +08:00 via Android
    也就是说,服务器作为客户端的时候,tomcat 是同步阻塞,而 netty 是同步非阻塞,我认为差距在这里。至于接入客户请求都是同步非阻塞。
    lhx2008
        23
    lhx2008  
       2019-11-17 13:10:42 +08:00 via Android
    当然我说的是 servlet 的旧标准,新标准也是可以异步返回的,那么和 netty 的差距就是在代码块的粒度上了,netty 更小,可以接入更多的连接。
    lhx2008
        24
    lhx2008  
       2019-11-17 13:11:50 +08:00 via Android
    至于跑分其实没有差距那么大。因为处理还是要用 cpu 时间的,netty 那种虽然接入了,但是几分钟才返回,百万 QPS 不挂,也没有意义的。
    iXingo
        25
    iXingo  
    OP
       2019-11-17 13:15:16 +08:00
    @lhx2008 那您能简单概括一下 vert.x 在 netty 的基础上做了那些东西吗
    sagaxu
        26
    sagaxu  
       2019-11-17 13:51:30 +08:00
    @iXingo vertx 和 netty 完全没有可比性,你拿 netty 和 vertx 分别写个个人博客就深有体会了
    zmxnv123
        27
    zmxnv123  
       2019-11-17 14:36:00 +08:00
    主要是网络 io,netty 也可以用来优化本地 io
    joooooker21
        28
    joooooker21  
       2019-11-17 15:57:26 +08:00
    可以先了解 NIO 的线程模型与 IO 线程模型的差异
    iXingo
        29
    iXingo  
    OP
       2019-11-17 17:00:29 +08:00
    谢谢各位老铁
    iXingo
        30
    iXingo  
    OP
       2019-11-17 17:01:16 +08:00
    @sagaxu vertx 底层是用的 Netty 吗
    mifly
        31
    mifly  
       2019-11-17 18:03:00 +08:00 via Android
    netty 是网络 io 框架,vertx 是开发框架,网络层用的还是 netty,不是同一个功能的东西,没有可比性
    iXingo
        32
    iXingo  
    OP
       2019-11-17 19:19:10 +08:00
    @mifly 好的
    dosmlp
        33
    dosmlp  
       2019-11-17 20:09:55 +08:00
    就是你不用等 io 操作,操作系统帮你做了,至于操作系统怎么做的,可能是硬中断吧
    micean
        34
    micean  
       2019-11-17 22:09:18 +08:00
    #11 +1

    不是异步 IO,而是 NIO,所以不是不会阻塞,而是不能阻塞!
    让每个线程不用浪费时间等待 IO 而去执行别的任务,所以 NIO 可以减少线程数量,减少上下文切换造成的性能损失
    luozic
        35
    luozic  
       2019-11-17 22:21:18 +08:00
    curd 应用,性能问题大部分在重量级的切换和轮询。
    beidounanxizi
        36
    beidounanxizi  
       2019-11-17 22:23:13 +08:00
    去学学更底层的书把 。netty 只是 恨 JAVA NIO 不成钢,另起灶台重新搭的。底层还是 linux io 哪套 不过 给你装备了好多工具, 方便你处理 IO
    xuanbg
        37
    xuanbg  
       2019-11-17 22:55:28 +08:00
    提升的原理就是快的连接不用等慢的连接处理完。就像地铁的安检,不带包的可以走快速通道不用在带包的队伍里面慢慢排队。
    iXingo
        38
    iXingo  
    OP
       2019-11-18 00:04:23 +08:00
    iXingo
        39
    iXingo  
    OP
       2019-11-18 00:06:12 +08:00
    @luozic 但是对于阻塞的 JDBC 等等业务上的阻塞还是没有办法的是吧
    iXingo
        40
    iXingo  
    OP
       2019-11-18 00:06:53 +08:00
    @micean 但是对于业务操作的阻塞的话还是没办法的是吧
    outoftimeerror
        41
    outoftimeerror  
       2019-11-18 01:06:32 +08:00
    业务操作阻塞了,netty 用了也没什么意义,比如你每个请求过来再用 jdbc 操作数据库,那效率依然不高。
    除非你全部非阻塞,就像 vert.x 用了异步的方式 vertx-jdbc-client 操作数据库。
    全部异步的难点在于,很多业务其实是需要同步的,而且代码写起来反人类,当然函数式编程倒是提供了优雅的解决方案:future monad。
    纯异步反人类,java 的线程太重,所以我觉得更实用的解决方案是 goroutine+channel。
    by73
        42
    by73  
       2019-11-18 01:17:54 +08:00
    我所知道的(碍于知识水平可能有误),阻塞与非阻塞是针对 I/O 传输到内存这部分而言,阻塞 I/O 在传输时全程需要 CPU 干预;非阻塞 I/O 在传输时 CPU 可以干别的事,直到像 DMA 这样的控制器给 CPU 发个完成的中断,再调用到操作系统的中断处理程序。

    而同步和异步则是针对进程 /线程而言的,假如调用 Files.readAllLines(),那么当前线程就会处于挂起状态,直到上面的阻塞 /非阻塞 I/O 把数据传递到内存之后才被恢复,这个过程其实对 CPU 没有影响,只是对你的线程有影响;异步就是当前线程不再等待 I/O 传输的完成,只是发起一个 I/O 请求和请求完之后要做的事情,然后交给另一个线程去负责(这样阻塞也就只会阻塞另外那个线程,不会影响当前线程的执行)。异步本质上可以理解为多线程。

    所以综上,回答题主的问题,非阻塞 I/O 提升的时间主要是数据传输到内存的这段 CPU 时间,这样 CPU 就能处理其他的进程;另外,异步 I/O 的价值在于“小而多”的请求,典型例子就是 HTTP,因为 I/O 特别多,如果每个线程都要去等待这些 I/O 完成,肯定会浪费其他非 I/O 的时间,将这些 I/O 交给另一个线程,自己就“解放了”;而对于 I/O 密集的任务来说,异步 I/O 没啥大优势,当 I/O 占很大一部分时,非 I/O 部分的运行时间就可以忽略了,基本上都是在排队阻塞、传输、调回调函数,整个系统的并发度也就降到了跟同步差不多的水平(大家都是在等 I/O )。

    (注意这里的所有解释都是针对 I/O 传输的过程,因为等待 I/O 到来这件事必须要等待,像 Socket.accept() 或者 listen() 都是要强行阻塞的)

    (熬夜过程可能脑子不太清醒,见谅,也希望能指出错误)
    by73
        43
    by73  
       2019-11-18 01:45:21 +08:00 via Android
    @by73 噢对了,Java/Netty 的 NIO 指的是 New I/O,具体而言指的是异步 I/O,而不是 Non-blocking,因为 I/O 阻塞的话语权不在它们那。
    des
        44
    des  
       2019-11-18 08:36:07 +08:00 via Android   ❤️ 3
    打个比方,好比如双十一代购
    同步阻塞就是,你买了东西,等快递到了再去做其他事情,等待的时候啥也不做
    非阻塞就是,你买了东西,你自己过一会去看一下到了没,过一会看一下
    异步就是,买了东西,人家短信通知你,你可以做其他事情去。其中还有水平和边缘触发的区别,也很简单,一个是你有快递了通知你一下,另一个是你有快递了一直通知你
    至于省出来的时间当然是,你在那里瞎等的时间或者自己去查状态的时间。
    什么,你说自己查状态也不费时间?那假如你有 1w 个呢,每一个都要查一遍呢?

    另外这是建立在,你的工作本身就是处理这些快递的前提下的,也就是 io 密集,类似 curd (怎么感觉像是在黑 curd

    如果是 cpu 密集,那就完全不一样了。
    你需要花很多时间去处理相关的东西(与 io 无关的),比方说,快递到了要组装好才能再给代购的人,这期间你是没法处理其他快递的

    所以问题就很明显了,省下来时间就是在那里干等白白流逝的时间,本来你还能接更多单的。
    至于说人家快递发的慢,那你也没办法是不是(指 io 本身慢以及 io 处理对方慢),但是你还是能服务到更多人是不是?


    同步阻塞的另一种方案是,开多个线程去等,就好比限购,你只能找多个人一起买,然后每个人还是慢慢等
    这个好像在客户端程序开发多见一点,毕竟异步写起来还是比较复杂的
    des
        45
    des  
       2019-11-18 08:40:32 +08:00 via Android
    @des
    最近死亡搁浅看多了,第一个想到的就是快递
    可能有些东西不够准确,但是基本就是这些原理了
    如果说的有不对的地方,希望大家能指正
    micean
        46
    micean  
       2019-11-18 10:25:05 +08:00
    @iXingo

    阻塞的业务放在放在业务线程,别放在 io 线程
    w0000
        47
    w0000  
       2019-11-18 10:28:17 +08:00
    对于这种优化应该从两种情况来看,第一种是少量连接,每次请求的数据多,第二种是大量连接,每次请求的数据量小。
    netty 对第二种情况的优化是比较大的,用少量线程去轮训处理连接,但是对第一种情况是没有优化的,甚至会有轮训的性能损失。
    qiyuey
        48
    qiyuey  
       2019-11-18 10:53:14 +08:00
    配合 Kotlin Coroutines 或者 Reactive 效果更佳
    iXingo
        49
    iXingo  
    OP
       2019-11-18 12:40:05 +08:00
    @by73 感谢您的回答, 还有其实异步回调函数就是把原本主线程等待 I/O 连接 /传输过程之后的操作放在其他线程上, 其实本质上就是多线程的一种操作,是否可以这么理解
    iXingo
        50
    iXingo  
    OP
       2019-11-18 12:42:14 +08:00
    @w0000 请问 netty 对于大量连接和数据量也大的情况,其实是可以并发处理连接,但是数据量受限于操作系统和物理限制,也是没法做更大的优化,是否可以这么理解
    iXingo
        51
    iXingo  
    OP
       2019-11-18 12:51:44 +08:00
    @des 如果按照您这样说的, Netty 做的就是包装了 JDK 的 NIO 方法, 相当于一个菜鸟驿站的快递接收窗口(可能不恰当), 原先你要需要对每个快递进行等待, 现在你每次只要和这个窗口进行对接, 你每次让他帮你查询一下(底层调用一下 select()方法), 你知道有哪些快递已经到了, 你就只要对已经到达的快递进行处理. 可以这么理解吗
    by73
        52
    by73  
       2019-11-18 13:18:27 +08:00
    @iXingo 对,本质上就是让另一个线程去处理。至少 java.nio.channels 是这样做的,如果你带了一个回调函数,那么

    > The completion handler for an I/O operation initiated on a channel bound to a group is guaranteed to be invoked by one of the pooled threads in the group.

    (摘自 JavaDoc ),就是说你的回调函数会在 I/O 结束之后被异步处理的线程调用。
    a570295535
        53
    a570295535  
       2019-11-18 13:35:19 +08:00
    @des 写的很不错👍
    iXingo
        54
    iXingo  
    OP
       2019-11-18 15:29:02 +08:00
    @by73 好的,感谢
    iIli1iIliIllLiL
        55
    iIli1iIliIllLiL  
       2019-11-22 14:05:49 +08:00
    需要了解下异步与同步非阻塞的区别
    mazai
        56
    mazai  
       2019-12-02 19:03:24 +08:00
    可以去看下 Reactor 模式,看完以后你就会明白了,且 netty 对 NIO 进行了改进和增强,以及封装了一些协议,实现了 zero-copy 等,这就是他强大的地方。吊打其他同类型框架。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3445 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 11:56 · PVG 19:56 · LAX 03:56 · JFK 06:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.