zoukankan      html  css  js  c++  java
  • Netty (1)

    Netty(1)

    官网的介绍,Netty 是一个高性能、异步事件驱动的 NIO 框架,它提供了对 TCP、UDP 和文件传输的支持,作为一个异步 NIO 框架,Netty 的所有 IO 操作都是异步非阻塞的,通过 Future-Listener 机制,用户可以方便的主动获取或者通过通知机制获得 IO 操作结果。

    作为当前最流行的 NIO 框架,Netty 在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,一些业界著名的开源组件也基于 Netty 的 NIO 框架构建。

    传统的阻塞I/O模型(BIO)

    特点

    • 采用阻塞式 I/O 模型获取输入数据;

    • 每个连接都需要独立的线程完成数据输入,业务处理,数据返回的完整操作。

    问题

    • 当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大;

    • 连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read 操作上,造成线程资源浪费。

    普通的小项目,个人开发者可以用这种玩一玩,但是在大量并发的面前,最终会耗尽资源。

    事件驱动模型

    事件驱动⽅式, 发⽣事件,主线程把事件放⼊事件队列,在另外线程不断循环消费事件列表中的事
    件,调⽤事件对应的处理逻辑处理事件。事件驱动⽅式也被称为消息通知⽅式,其实是设计模式中
    观察者模式的思路。

    O'Reilly ⼤神关于事件驱动模型解释图

    事件驱动模型主要包括 4 个基本组件:
    事件队列(event queue): 接收事件的入口,存储待处理事件。
    分发器(event mediator): 将不同的事件分发到不同的业务逻辑单元。
    事件通道(event channel): 分发器与处理器之间的联系渠道。
    事件处理器(event processor): 实现业务逻辑,处理完成后会发出事件,触发下⼀步操作。

    相对传统轮询模式,事件驱动有如下优点:
    可扩展性好, 分布式的异步架构,事件处理器之间高度解耦,可以方便扩展事件处理逻辑。
    高性能, 基于队列暂存事件,能方便并行异步处理事件。

    Netty高效的 Reactor 线程模型

    Reactor单线程模型

    Reactor单线程模型,使用异步非阻塞I/O,理论上一个线程可以独立处理所有 IO 相关的操作

    从架构层面看,一个 NIO 线程确实可以完成其承担的职责。例如,通过 Acceptor 接收客户端的 TCP 连接请求消息,链路建立成功之后,通过Dispatch将对应的ByteBuffer派发到指定的Handler 上进行消息解码。用户 Handler可以通过NIO线程将消息发送给客户端。

    小规模场景,Reactor单线程可以解决,但是对于高负载大并发的场景缺不适合。

    1. 一个NIO 线程同时处理成百上千的链路性能上无法支撑,即便 NIO 线程的 CPU 负荷达到 100%,也无法满足海量消息的编码、解码、读取和发送;
    2. 当NIO线程满载或者过载,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这种负反馈效应会使得系统负担加重,结果就是消息积压和处理超时。
    3. 一旦NIO线程出现问题,无法处理I/O,会使得整个系统的通信崩溃,无法接收和处理消息,可靠性太差。

    Reactor多线程模型

    多线程的演化,解决了单线程中的一些问题,虽然还是以一个NIO Acceptor线程处理监听服务端,接收客户端TCP链接,但是后续的I/O读写,则交给了Reactor线程池处理

    1. 线程池负责业务处理,消息读取,encode,decode

    2. 一个NIO线程可以处理 N 条链路,但是 1 个链路只对应 1 个 NIO 线程,防止发生并发操作问题

    虽然多线程解决了部分问题,但是负责监听和链接的依然是一个NIO线程,在高并发场景下依然存在风险。

    Reactor主从多线程模型

    多线程的Reactor可以满足大多数场景,但是如果面对高并发,就会暴露短板。

    于是出现了Reactor主从多线程

    负责接收客户端连接的从一个线程,变成了的Main Reactor Pool,所有的客户端请求经过main accept pool处理后,将新建的Channel注册到sub reactor pool,这样main pool负责了auth,login,shakehand,SLA等,而链路建立成功后就会有sub pool来负责I/O。

    Netty特性

    多路复用模型

    Netty在NioEventLoop内封装了Selector来实现了I/O的多路复用。

    在服务端可以同时并发处理大量的客户端请求,同事由于读写操作都是异步非阻塞,因此大大提高了I/O的效率,避免由于阻塞导致的线程挂起和资源浪费。

    数据零拷贝

    linux将系统程序和用户程序的资源隔离,将系统分为了内核态和用户态。

    这样用户的应用程序只能在用户态,无法直接获取内核资源,就只能从系统接口获取。

    普通的数据拷贝会有几次处理:

    1. 磁盘到内核的buffer

    2. 内核buffer到用户buffer

    3. 用户buffer到内核的socket buffer

    4. socket buffer到网卡的buffer

    在传统的处理方式就会经历以上4步,但是这样会非常影响系统性能。

    而Netty的数据接收,发送直接使用堆外直接内存进行socket读写,通过DMA处理,就避免了用户buffer来回拷贝带来的损耗。

    无锁化设计

    并行多线程会提高系统的效率,但是随之而来的风险也是不容忽视的,如果对共享资源访问处理不当,会带来严重的锁竞争,导致性能下降,而频繁的线程切换也会使得性能有损失。

    Netty通过串行无锁化设计,即将消息的处理放在同一个线程内完成,在线程内部进行串行操作,避免线程竞争。而且可以通过调整NIO线程池的参数,同时启动多个串行化的线程并行运行。

    高性能的序列化框架

    使用的是google的protobuf实现序列化,

    Nice,又水了一篇,Netty虽然不会直接使用,但是其中的设计思想,很值得学习。而且很多的RPC框架都采用了这个NB的框架。

  • 相关阅读:
    HDU.5765.Bonds(DP 高维前缀和)
    SPOJ.TLE
    LOJ.2585.[APIO2018]新家(二分 线段树 堆)
    BZOJ.2679.Balanced Cow Subsets(meet in the middle)
    BZOJ.3293.[CQOI2011]分金币(思路)
    BZOJ.4558.[JLOI2016]方(计数 容斥)
    BZOJ.3631.[JLOI2014]松鼠的新家(树上差分)
    BZOJ.1568.[JSOI2008]Blue Mary开公司(李超线段树)
    BZOJ.1071.[SCOI2007]组队(思路)
    BZOJ.4910.[SDOI2017]苹果树(树形依赖背包 DP 单调队列)
  • 原文地址:https://www.cnblogs.com/dreamtaker/p/14491411.html
Copyright © 2011-2022 走看看