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

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

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

    不是异步 IO,而是 NIO,所以不是不会阻塞,而是不能阻塞!
    让每个线程不用浪费时间等待 IO 而去执行别的任务,所以 NIO 可以减少线程数量,减少上下文切换造成的性能损失
        35
    luozic   21 天前
    curd 应用,性能问题大部分在重量级的切换和轮询。
        36
    beidounanxizi   21 天前
    去学学更底层的书把 。netty 只是 恨 JAVA NIO 不成钢,另起灶台重新搭的。底层还是 linux io 哪套 不过 给你装备了好多工具, 方便你处理 IO
        37
    xuanbg   21 天前
    提升的原理就是快的连接不用等慢的连接处理完。就像地铁的安检,不带包的可以走快速通道不用在带包的队伍里面慢慢排队。
        38
    iXingo   21 天前
        39
    iXingo   21 天前
    @luozic 但是对于阻塞的 JDBC 等等业务上的阻塞还是没有办法的是吧
        40
    iXingo   21 天前
    @micean 但是对于业务操作的阻塞的话还是没办法的是吧
        41
    outoftimeerror   21 天前
    业务操作阻塞了,netty 用了也没什么意义,比如你每个请求过来再用 jdbc 操作数据库,那效率依然不高。
    除非你全部非阻塞,就像 vert.x 用了异步的方式 vertx-jdbc-client 操作数据库。
    全部异步的难点在于,很多业务其实是需要同步的,而且代码写起来反人类,当然函数式编程倒是提供了优雅的解决方案:future monad。
    纯异步反人类,java 的线程太重,所以我觉得更实用的解决方案是 goroutine+channel。
        42
    by73   21 天前
    我所知道的(碍于知识水平可能有误),阻塞与非阻塞是针对 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() 都是要强行阻塞的)

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

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

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

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


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

    阻塞的业务放在放在业务线程,别放在 io 线程
        47
    w0000   20 天前
    对于这种优化应该从两种情况来看,第一种是少量连接,每次请求的数据多,第二种是大量连接,每次请求的数据量小。
    netty 对第二种情况的优化是比较大的,用少量线程去轮训处理连接,但是对第一种情况是没有优化的,甚至会有轮训的性能损失。
        48
    qiyuey   20 天前
    配合 Kotlin Coroutines 或者 Reactive 效果更佳
        49
    iXingo   20 天前
    @by73 感谢您的回答, 还有其实异步回调函数就是把原本主线程等待 I/O 连接 /传输过程之后的操作放在其他线程上, 其实本质上就是多线程的一种操作,是否可以这么理解
        50
    iXingo   20 天前
    @w0000 请问 netty 对于大量连接和数据量也大的情况,其实是可以并发处理连接,但是数据量受限于操作系统和物理限制,也是没法做更大的优化,是否可以这么理解
        51
    iXingo   20 天前
    @des 如果按照您这样说的, Netty 做的就是包装了 JDK 的 NIO 方法, 相当于一个菜鸟驿站的快递接收窗口(可能不恰当), 原先你要需要对每个快递进行等待, 现在你每次只要和这个窗口进行对接, 你每次让他帮你查询一下(底层调用一下 select()方法), 你知道有哪些快递已经到了, 你就只要对已经到达的快递进行处理. 可以这么理解吗
        52
    by73   20 天前
    @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 结束之后被异步处理的线程调用。
        53
    a570295535   20 天前
    @des 写的很不错👍
        54
    iXingo   20 天前
    @by73 好的,感谢
        55
    iIli1iIliIllLiL   16 天前
    需要了解下异步与同步非阻塞的区别
        56
    mazai   6 天前
    可以去看下 Reactor 模式,看完以后你就会明白了,且 netty 对 NIO 进行了改进和增强,以及封装了一些协议,实现了 zero-copy 等,这就是他强大的地方。吊打其他同类型框架。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   913 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 31ms · UTC 18:53 · PVG 02:53 · LAX 10:53 · JFK 13:53
    ♥ Do have faith in what you're doing.