我们客户端连接服务端请求本质就是通过Socket来进行连接

客户端会有一个Socket接口,服务端也会有一个Socket接口

这时候服务端需要去磁盘中查询数据,那数据没有查询完怎么办呢?其他客户端就需要一直等待查询完成

这也就是同步阻塞,也就是BIO

客户端连接服务端中间会有一个通道叫做SocketChannel

服务端会有一个轮询器

这时候会将SocketChannel注册到服务端的轮询中,每有一个客户端,就会有一个Channel注册到轮询器中,轮询器中会有一个线程不断的轮询,查看这个通道的状态

当有一个通道可连接就会把这个Channel拿出来改写状态,将其从可连接状态改成可读状态再注册到轮询器中,然后不断的轮询,发现可读了,就可以拿出来读取了

这时候会有一个问题,Channel的状态通常都是放到内核空间中, 轮询的每次查找都是从用户空间切换到内核空间

优化方案就是把fd文件描述符传给内核,让内核自己来查找,直到状态可连接或者可读就把他拿到用户空间,再返回到轮询器,这样就只有传入时从用户空间切换到内核空间,和结束时从内核空间传入到用户空间的两次过程了

Reactor 模式是一种事件驱动的并发编程模型,用于高效处理大量 I/O 请求(如网络连接)。其核心思想是通过 I/O 多路复用技术监听多个事件源(如 Socket),并在事件就绪时同步分发给对应处理器,避免传统阻塞 I/O 的资源浪费。

例如Redis、Netty、Nginx、Nodejs就是采用了Reactor模式

那么这个优化的过程就叫做多路复用,此时的轮询器也就是select和poll

Select传fd描述符到内核时,数量有限制,而poll没有

新的问题又产生了,这时候内核也仍然需要循环遍历去监听状态,那有没有方法能够解决这个问题,让它能够在Channel可连接/可读时直接找到呢?

这就是epoll

首先我们要知道

连接分为以下几个步骤:

创建好ServerSocket

绑定好端口号bind

再进行监听

epoll会调用epoll_create命令

此时会创建两个空间,一个红黑树一个双向链表,再调用epoll_ctl,将fd描述符,放进红黑树里监听可连接事件,再调用epoll_wait到双向链表中拿fd描述符,那什么时候链表中存在fd描述符的呢?

网卡接受到数据时,内核会检查Socket变化,网络设备驱动会通过内核将数据放到内核中的fd缓冲区,内核就会检查fd的文件描述符,发现可连接了,就会拿出来放到双向链表中,这就是整个NIO的过程

附上NIO代码

NIO和AIO区别在于,NIO更多还是需要程序自己来执行后续操作,这就是同步操作,AIO是内核遇到可读可写事件后,由内核主动的去做,这就是异步

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐