Netty学习内容

java中的几种I/O模型

BIO通信模型

BIO通信模型图

Netty学习内容_第1张图片
BIO通信模型

网络编程的基本模型是C/S模型,两个进程之间进行通信,需要服务端提供位置信息(绑定的IP和监听端口),客户端通过连接操作向服务端监听的地址发起请求,通过三次握手建立连接。

在同步阻塞的通信模型中,由一个独立的Acceptor线程监听客户端的连接,它接收到客户端的连接请求后就为每个客户端创建一个新的线程进行链路处理,处理完成后通过输出流返回应答给客户端,然后线程销毁。

通过以上模型可以看出,该通信模式最大的弊端在于,当客户端的并发访问量增大时,服务端创建的连接数与客户端的并发请求数为正比关系,线程数过多会消耗jvm的资源,系统性能下降,导致无资源创建新线程、线程堆栈溢出等,最终导致系统无法对外提供服务。

伪异步IO通信模型

为了解决BIO的缺陷,对BIO进行优化产生了伪异步IO模型——后端通过一个线程池来处理多个客户端的请求接入。

Netty学习内容_第2张图片
image.png

采用线程池和任务队列进行实现伪异步IO通信。
当客户端发起请求时,Acceptor接收请求,但并不是直接进行线程创建,而是交由后面的线程池处理。JDK的线程池维护一个消息队列和N个活跃线程,对消息队列中的任务进行处理。
线程池中的线程数和消息队列的大小可以设置,所以资源可控,不会产生高并发访问时BIO模型耗尽系统资源的情况。

看起来伪异步的线程模型很好,但是仍然没有实质性解决同步IO导致的线程阻塞问题。
当通信对方返回的应答时间过长会导致以下问题:

  1. 服务端处理缓慢,返回应答消息耗时60s,平时只需要10ms;
  2. 才以哦那个伪异步IO的线程正在读取故障服务节点的响应,由于读取输入流是阻塞的,所以踏会被同步阻塞60s;
  3. 假如所有的可用线程都被故障服务器阻塞,那后续的IO消息都在队列中排队;
  4. 由于线程池采用阻塞队列实现,队列满时,后续入队列的操作都将被阻塞;
  5. 由于前端只有一个Acceptor线程接收客户端的请求,它被阻塞在线程池的同步阻塞队列之后,新的客户端请求都将无法响应,客户端后发生大量连接超时;
  6. 由于所有的连接都超时,调用者会认为系统崩溃,无法接收新的请求。

NIO通信模型

  1. 缓冲区 buffer
    传统IO面向流,可以将数据直接写入或者读取到stream对象中。
    在NIO中,所有数据都在缓冲区中进行处理。
  2. 通道channel
    channel是通道,网络数据通过channel进行读取和写入。通道与流的区别在于,流是单向的,通道是双向的,全双工的。
  3. 多路复用器selector
    selector会不断轮询注册在其上的channel,如果某个channel上发生读写事件,这个channel就处于就绪状态,会被selector轮询出来,然后通过selectionkey 可以获取就绪的channel的集合,进行后续的IO操作。
    一个selector可以同时轮询多个channel,并且jdk处采用了epoll()代替传统的selector实现,所以没有最大连接句柄的限制。也就意味着,理论上,一个线程负责selector的轮询,就可以接入成千上万的客户端。

NIO服务端序列图

Netty学习内容_第3张图片
image.png

NIO客户端序列图

Netty学习内容_第4张图片
image.png

Netty线程模型

  1. Reactor单线程模型
    Reactor单线程模型是指所有的IO操作均在一个NIO线程上处理。
Netty学习内容_第5张图片
image.png

由于Reactor模式使用的是异步非阻塞IO,所以理论上所有的IO操作都不会造成阻塞,一个线程可以处理所有的IO操作。
Acceptor接收客户端请求,连接创建后,由dispatcher分发到各个handler上进行处理,处理完后通过NIO线程返回给客户端。

  1. Reactor多线程模型
    与单线程模型最大的区别在于有一组NIO线程处理IO操作。
Netty学习内容_第6张图片
image.png

有一个专门的NIO线程——Acceptor负责监听服务端,接收客户端的TCP连接请求。

网络IO操作由一个NIO线程池负责,线程池包含一个任务队列和N个可用的线程,由这些NIO线程负责消息的读取、解码、编码、发送。

一个NIO线程可以同时处理N条链路。

  1. 主从Reactor模型
Netty学习内容_第7张图片
image.png

主从Reactor模型的特点是服务端接收客户端连接的不是一个NIO线程,而是一个独立的NIO线程池。Acceptor接收到客户端的TCP连接请求并处理完成后将新创建的channel注册到IO线程池的一个IO线程上,由它负责channel的读写和编码、解码工作。Acceptor线程池仅仅用于客户端的登录、握手和安全认证,一旦链路创建成功就将链路注册到后端线程池的IO线程上,由后端线程负责后续的操作。

总结:通过以上几种通信模型的分析可以看出,影响IO通信的性能有三个要素:IO模型、协议、线程。
Netty的多线程其实很类似于伪异步IO的解决方案,在Acceptor上做文章,将一个线程变为一个线程池。
主从reactor模型也就是在后台处理上采用了线程池的方式

你可能感兴趣的:(Netty学习内容)