当我们接收请求的时候,实际上请求在到达我们的程序之前,会有一个请求队列,所有请求都会先在请求队列中
BIO:同步阻塞
在BIO中会使用socket.accept()方法来一个一个的接收请求,由于accept方法是阻塞的,所以当我们接收到一个请求后,会给这个请求独立分配一个线程去处理这个请求,这种请求方式就是BIO
但是这里会有一个问题,如果我的链接是个空连接,并没有数据交互,他依然需要占用一个线程,并且随着连接数量变多,线程数量也会越来越多,服务器压力越来越大。
NIO:同步非阻塞
NIO中有三个模型,分别是select模型,poll模型,epoll模型
- select模型
在select模型中,所有的请求都是由同一个线程接收,之后再进行不同的处理,这个线程就叫做多路复用器,在select模型中,把每一个请求链接都当作一个channel对象,然后将每个channe对象注册到多路复用器中,然后多路复用器会对注册进来的channe进行循环遍历,当有数据到来时,会开启一个新的线程,来对该channel进行处理。select在监听channel对象是否有数据到来时,需要从用户态内存拷贝该文件的文件句柄到内核态内存中,交由底层的系统来监听,监听之后再拷贝回用户态中
这中处理方式就会对比BIO就会减少线程的占用,因为如果没有数据交互,我是没有必要开启线程进行处理的
- poll模型
poll模型与select模型的处理方式是一样的,区别就是poll模型的channel注册是没有数量限制的,但是select模型在注册到多路复用器时,一个多路复用器最多只能注册1024个请求
- epoll模型
在select模型中存在两个问题:
(1)多路复用器可以处理的请求数量过小,只有1024;
(2)select在监听channel对象是否有数据到来时,需要从用户态内存拷贝该文件的文件句柄到内核态内存中,交由底层的系统来监听,监听之后再拷贝回用户态中,这样需要进行两次拷贝
针对第一个问题,epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右
对于第二个问题,epoll模型增加了一个用户态和内核态都可以进行操作的公共内存区域,只需要将channel对象拷贝到这个公共的内存中就可以让底层系统进行监听,不需要进行第二次的拷贝,提高运行效率