Netty是一个高性能NIO框架,其是对Reactor模型的一个实现java
NIO模型web
Acceptor注册Selector,监听accept事件 当客户端链接后,触发accept事件 服务器构建对应的Channel,并在其上注册Selector,监听读写事件 当发生读写事件后,进行相应的读写处理
Reactor中的组件编程
Reactor:Reactor是IO事件的派发者。 Acceptor:Acceptor接受client链接,创建对应client的Handler,并向Reactor注册此Handler。 Handler:和一个client通信的实体,按这样的过程实现业务的处理。通常在基本的Handler基础上还会有更进一步的层次划分, 用来抽象诸如decode,process和encoder这些过程。好比对Web Server而言,decode一般是HTTP请求的解析, process的过程会进一步涉及到Listener和Servlet的调用。业务逻辑的处理在Reactor模式里被分散的IO事件所打破, 因此Handler须要有适当的机制在所需的信息还不全(读到一半)的时候保存上下文,并在下一次IO事件到来的时候(另外一半可读了)能继续中断的处理。为了简化设计,Handler一般被设计成状态机,按GoF的state pattern来实现。后端
对应上面的NIO代码来看: Reactor:至关于有分发功能的Selector Acceptor:NIO中创建链接的那个判断分支 Handler:消息读写处理等操做类缓存
Reactor单线程模型服务器
这个模型和上面的NIO流程很相似,只是将消息相关处理独立到了Handler中去了! 虽然上面说到NIO一个线程就能够支持全部的IO处理。可是瓶颈也是显而易见的!咱们看一个客户端的状况,若是这个客户端屡次进行请求,若是在Handler中的处理速度较慢,那么后续的客户端请求都会被积压,致使响应变慢!因此引入了Reactor多线程模型!网络
Reactor多线程模型多线程
Reactor多线程模型就是将Handler中的IO操做和非IO操做分开,操做IO的线程称为IO线程,非IO操做的线程称为工做线程!这样的话,客户端的请求会直接被丢到线程池中,客户端发送请求就不会堵塞! 可是当用户进一步增长的时候,Reactor会出现瓶颈!由于Reactor既要处理IO操做请求,又要响应链接请求!为了分担Reactor的负担,因此引入了主从Reactor模型!并发
主从Reactor模型框架
主Reactor用于响应链接请求,从Reactor用于处理IO操做请求
几种IO模型总结 BIO:同步阻塞式通讯 伪异步I/O:为了解决同步阻塞 I/O 面临的一个链路须要一个线程处理的问题,后来有人 对它的线程模型进行了优化,后端经过一个线程池来处理多个客户端的请求接入, 造成客户端个数 M:线程池最大线程数 N 的比例关系,其中 M 能够远远大于 N, 经过线程池能够灵活的调配线程资源,设置线程的最大值,防止因为海量并发接 入致使线程耗尽。 若是通讯对方返回应答时间过长,会引发的级联故障
NIO:它的官方叫法叫NewI/O。可是,因为以前老的 I/O 类库是阻塞 I/O,New I/O 类 库的目标就是要让 Java 支持非阻塞 I/O,因此,更多的人喜欢称之为非阻塞 I/ O(Non-block I/O) AIO:NIO2.0 引入了新的异步通道的概念,并提供了异步文件通道和异步套接字 通道的实现。 异步通道提供两种方式获取获取操做结果: • 经过java.util.concurrent.Future类来表示异步操做的结果; • 在执行异步操做的时候传入一个java.nio.channels; CompletionHandler接口的实现类做为操做完成的回调。 NIO2.0(AIO) 的异步套接字通道是真正的异步非阻塞 I/O,它对应UNIX网络编程 中的事件驱动 I/O(AIO asynchronous I/O),它不须要经过多路复用器(Selector)对注册的通 道进行轮询操做便可实现异步读写,从而简化了 NIO 的编程模型。
epoll能够理解为event poll,不一样于忙轮询和无差异轮询,epoll之会把哪一个流发生了怎样的I/O事件通知咱们。此时咱们对这些流的操做都是有意义的。(复杂度下降到了O(1)) epoll提供了三个函数,epollcreate,epollctl和epollwait,epollcreate是建立一个epoll句柄;epollctl是注册要监听的事件类型;epollwait则是等待事件的产生
elect,poll,epoll都是IO多路复用的机制。I/O多路复用就经过一种机制,能够监视多个描述符,一旦某个描述符就绪(通常是读就绪或者写就绪),可以通知程序进行相应的读写操做。 但select,poll,epoll本质上都是同步I/O,由于他们都须要在读写事件就绪后本身负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需本身负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
使用netty而不用原生NIO缘由: 1,NIO的类库和API繁杂,使用麻烦 2,须要具有其余的额外技能作铺垫,例如熟悉Java多线程编程。这是由于 NIO编程涉及到Reactor模式,你必须对多线程和网路编程很是熟悉,才能 编写出高质量的NIO程序。 3,可靠性能力补齐,工做量和难度都很是大。例如客户端面临断连重连、网 络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等问题,NIO 编程的特色是功能开发相对容易,可是可靠性能力补齐的工做量和难度都 很是大。 4,JDK NIO的BUG,例如臭名昭著的epoll bug,它会致使Selector空轮询, 最终致使CPU 100%。官方声称在JDK1.6版本的update18修复了该问题,但 是直到JDK1.7版本该问题仍旧存在,只不过该BUG发生几率下降了一些而 已,它并无被根本解决。
不依赖于web容器的http服务
首先设想一下咱们目前的通讯方式,使用netty mina等异步事件驱动的通讯框架,将Channel中信息都分发到Handler中去处理了,Handler中的send方法只负责不断的发送消息,receive方法只负责不断接收消息,这时候就产生一个问题: 客户端如何对应同一个Channel的接收的消息和发送的消息之间的匹配呢? 这也很简单,就须要在发送消息的时候,必需要产生一个请求id,将调用的信息连同id一块儿发给服务器端,服务器端处理完毕后,再将响应信息和上述请求id一块儿发给客户端,这样的话客户端在接收到响应以后就能够根据id来判断是针对哪次请求的响应结果了。