网络IO发展历程:BIO、NIO、多路复用器、epoll

网络侧IO,通过网络来通信(偏向内核方面)
C10K问题:http://www.kegel.com/c10k.html#frameworks

BIO NIO 多路复用器

什么是NIO
操作系统角度:NIO表示非阻塞IO,nonblocking;
JDK角度:N表示New IO(要掌握nonblocking,channel,buffer,selector),内存、文件系统
要想明白JDK的NIO,要先明白操作系统的NIO

今天讲一些词汇:同步、异步、阻塞、非阻塞
网络IO发展历程:BIO、NIO、多路复用器、epoll_第1张图片

1、OS

程序不能直接访问内核,用户程序想要调用内核的方法,需要使用int 80中断,CPU从用户态切换到内核态。操作系统给内核暴露了几百个系统调用syscall
IDT:中断描述符表,记录0~255中断号,在BIOS就加载了,内核进行callback,比如int 0x80,这里的80是中断号
GDT:全局描述符表。
关于中断的概念:使用晶振(将直流电流转换为…打点),实现CPU的时间分片。中断分为软中断(多个进程的切换,让他们看似同时在执行)、硬件中断(接受键盘、鼠标输入)。进程想要访问硬件,需要触发int 0x80中断
宏观并行、微观串行
网络IO发展历程:BIO、NIO、多路复用器、epoll_第2张图片
新版图:
网络IO发展历程:BIO、NIO、多路复用器、epoll_第3张图片

举例

来看一段简单的可以进行网络通信的java代码(JDK1.4),来说明计算机的通信是怎么完成的

网络IO发展历程:BIO、NIO、多路复用器、epoll_第4张图片
抓取程序和内核之间的系统调用,追踪每一个线程,并输出到一个文件中:
strace用来监控指定的程序(字节码)发生了哪些系统调用
在这里插入图片描述
运行后生成的输出文件:
网络IO发展历程:BIO、NIO、多路复用器、epoll_第5张图片
JVMC语言开发的,跑在操作系统之上,进行了系统调用。
下面是输出了java代码在内核中实际进行的系统调用:
2541行的3就是java代码中 new 出的ServerSocket(),在3上绑定了端口号
最后一行看到accept(3,证明调用被阻塞了
网络IO发展历程:BIO、NIO、多路复用器、epoll_第6张图片
使用tail一直盯着这个输出,同时:开一个窗口,建立连接、发数据
网络IO发展历程:BIO、NIO、多路复用器、epoll_第7张图片
使用nc手工建立一个新的连接:
在这里插入图片描述
查看输出:
在这里插入图片描述
recv(5,是再一次阻塞,
网络IO发展历程:BIO、NIO、多路复用器、epoll_第8张图片
再用客户端 随意输入一些数据,再查看输出:
网络IO发展历程:BIO、NIO、多路复用器、epoll_第9张图片
查看server端的端口监听

网络IO发展历程:BIO、NIO、多路复用器、epoll_第10张图片

关于文件描述符:linux一切皆文件
文件描述符3:用来代表监听
文件描述符3,4:一个是ipv4,一个是ipv6

(补充)使用jps命令可以查看java线程号12855
网络IO发展历程:BIO、NIO、多路复用器、epoll_第11张图片

建立客户端连接之后,fd文件描述符里面多出一个5,代表新建立的这个连接:
网络IO发展历程:BIO、NIO、多路复用器、epoll_第12张图片
上述这种情况,如果一个客户端连接之后一直不发数据,会一直阻塞。别人无法连接进来。
那么,如何实现能够接受多个客户端?
传统的BIO模型:让一个连接对应一个线程,每新开一个连接,就给它新开一个线程。
弊端:线程过多

如何解决C10K问题?
C10K问题:用户态和内核态之间频繁切换。CPU在一个时间片只能处理一个程序(线程),线程具有独立的栈,当线程过多时,调度的时候成本较高。
问题的本质:因为阻塞(BIO)。为了解决阻塞,才开启的多线程。想解决这个问题,从内核的角度考虑,可以考虑让系统调用得到的文件描述符35变成非阻塞。
解决问题的思路:将阻塞变为不阻塞。这种功能应该在内核实现,而不是用程序实现。

不要受到一些文章的影响,简单的用“加机器”的方式解决问题。
将单击性能已经压榨到极限时,再考虑加机器,分布式/用集群。

我们看内核的系统调用
内核有一种非阻塞模式:在accept(3,中空转,要么返回client的描述符,要么返回-1
网络IO发展历程:BIO、NIO、多路复用器、epoll_第13张图片

使用了NIO的新代码
(补充代码图)
网络IO发展历程:BIO、NIO、多路复用器、epoll_第14张图片
网络IO发展历程:BIO、NIO、多路复用器、epoll_第15张图片
用了NIO的api,不能在JDK1.4中使用。可以在JDK1.8中使用。
ss.configBlocking(false);设置非阻塞
后面用一个循环,每一次循环中,如果没有连接,则打印null;如果有连接,则打印端口号。这样

网络IO发展历程:BIO、NIO、多路复用器、epoll_第16张图片

运行后,看循环输出的null可以判断,没有进入阻塞状态
在这里插入图片描述
accept一直都是-1,没有阻塞。代表没有连接。
网络IO发展历程:BIO、NIO、多路复用器、epoll_第17张图片
连接进来一个客户端之后:
网络IO发展历程:BIO、NIO、多路复用器、epoll_第18张图片
用一个线程可以接受很多客户端。(NIO:NonBlocking)
网络IO发展历程:BIO、NIO、多路复用器、epoll_第19张图片

NIO的存在的问题

放大
C10K当并发量很大的时候,文件描述符会很多,每循环一次,都要调用一次recv系统调用(复杂度O(n)),进行用户态到内核态的切换,切换时性能损耗大。
缩小
当C10K只有1个C发来了数据,只使用到了1个有用的系统调用,剩余n-1次的监听都是无效的->技术选型失败。(架构师进行技术选型:要懂原理)

如何解决:多路复用
网络IO发展历程:BIO、NIO、多路复用器、epoll_第20张图片

多路复用

多条路复用了一次调用得到具体的到来情况
实现方式:内核
网络IO发展历程:BIO、NIO、多路复用器、epoll_第21张图片
允许程序去调用内核,来监控更多的客户端,直到一个或多个文件描述符是可用的状态,再返回。减少了用户态、内核态的无用切换。
select多路复用器返回给程序一个list,告诉程序哪些可以读取,然后程序要自己读取。这是同步模型。

如果程序自己读取,程序在IO上的模型就叫同步模型
如果程序把读取的过程交给内核,自己做自己的事情,叫异步模型
网络IO发展历程:BIO、NIO、多路复用器、epoll_第22张图片
网络IO发展历程:BIO、NIO、多路复用器、epoll_第23张图片
多路复用的问题:如果有很多长连接,内核每次都要给程序传递很多连接对象
网络IO发展历程:BIO、NIO、多路复用器、epoll_第24张图片

网络IO发展历程:BIO、NIO、多路复用器、epoll_第25张图片

解决方式:epoll

epoll

Epoll也是多路复用器。它不负责读取IO,只关心返回结果。Epoll是Event poll,把有数据这个事件通知给程序,还需要程序自己取读取数据。
man epoll查看命令帮助文档
epoll_create, epoll_ctl, epoll_wait
网络IO发展历程:BIO、NIO、多路复用器、epoll_第26张图片

网络IO发展历程:BIO、NIO、多路复用器、epoll_第27张图片

监控redis用的是哪种系统调用
网络IO发展历程:BIO、NIO、多路复用器、epoll_第28张图片
网络IO发展历程:BIO、NIO、多路复用器、epoll_第29张图片

网络IO发展历程:BIO、NIO、多路复用器、epoll_第30张图片
netty是同步IO,异步计算

你可能感兴趣的:(Java)