8.6 非阻塞网络 I/O
1.计算机之间如何进行网络请求?
Socket,端口:
2.服务端--客户端
########################################################################
2.多线程服务端--客户端
########################################################################
3.线程池服务器
4.BIO:Blocking I/O 阻塞I/O
阻塞I/O: I/O操作时,用户线程i一直阻塞,直到写操作后者读操作完成。
5.Socket接收数据,系统内核的处理过程
解析:阻塞点:1.CPU拷贝数据期间,线程不被唤醒,处于阻塞状态。
2.接收缓冲区数据处理完毕,没有数据,线程再次被阻塞。
特点:线程A大部分时间处于被阻塞状态<==网络数据处理比较慢(几百毫秒----几秒钟),资源利用率比较低。
问题:如何提高性能?
分析:不要使线程阻塞,不要使用很多的线程处理网络通信。是否可以使用一个线程以非阻塞方式处理所有并发请求?
线程读写操作不阻塞。
6.非阻塞IO:Non-Blocking I/O
非阻塞I/O:I/O操作立即返回,发起线程不会阻塞等待。
非阻塞read操作:
Socket接收缓冲区有数据,读n个(不保证数据被读完整,因此可能需要多次读)
Socket缓冲区没数据,则返回失败(不会等待)
非阻塞write:
Socket发送缓冲区满,返回失败(不会等待)
Socket发送缓冲区不满,写n个数据(不保证一次性全部写入,因此可能需要多次写。)
7.阻塞IO VS 非阻塞IO
ServerSocket serverSocket=new ServerSocket(port);
while(true){
final Socket socket=serverSocket.accept();//等待请求进来。没有请求进来,当前线程则阻塞在这里。
new Thread( new Runnable{
@Override
public void run(){
InputStream in=socket.getInputStream();
OutputStream out=socket.getOutputStream();
byte[] content=new byte[1024];
int len;
StringBuilder sb=new StringBuilder();
while(len=in.read(content)!=-1){
//读取数据。如果没有数据,当前线程则阻塞在这里,等待数据。
//当数据到达,网卡通知CPU,CPU拷贝数据到接收缓冲区。
//拷贝完成,唤醒线程。读取线程,处理数据。
//线程被阻塞后,不能复用。
sb.append(new String(content,0,len));
}
out.write("Receive Success!\n"+sb.getBytes());
out.flush();
socket.shutdownOutput();
}
}).start();
}
InetSocketAddress address=new InetSocketAddress(8888);
ServerSocketChannel server=ServerSocketChannel.open();//启动server
server.bind(address);
server.configureBlocking(false);//配置非阻塞
Selector=Selector.open();
server.register(selector,SelectionKey.OP_ACCEPT);
while(true){
selector.select();//检查selector注册事件,哪些事件到达selector。
Iterator<SelectionKey> keys=selector.selectedKeys().iterator();//事件到达,获取selectionKey.
while(keys.hasNext(){
SelectionKey key=keys.next();
ByteBuffer buffer=ByteBuffer.allocate(1024);
if(key.isAcceptable()){//Accept事件发生。
ServerSocketChannel serverChannel=(ServerSocketChannel)key.channel();//找到Server
SocketChannel channel=serverChannel.accept();//建立新的通道。
channel.configureBlocking(false);//新的通道上配置非阻塞方式。
channel.register(selector,SelectionKey.OP_READ);//新的通道上注册新的事件:Read事件。监听Read事件
}else if(key.isReadable()){
SocketChannel channel=(SocketChannel)key.channel();//Read事件通道
channel.read(buffer);
........................................
channel.register(selector,SelectionKey.OP_WRITE);//通道注册Write事件
}else if(key.isWritable()){
SocketChannel channel=(SocketChannel)key.channel();//通道
channel.write(buffer);
........................................
channel.register(selector,SelectionKey.OP_READ);//写入完毕,注册READ事件
}
keys.remove();
}
}
8.系统IO复用方式:select,poll,epoll
9.select(poll)管理下的read过程
解析:selector遍历所有sokect,判断socket上发生的事件。(selector不知道事件发生在那个socket上)
缺点:高并发场景:1W+并发,selector遍历所有sokect,消耗很多资源。
10.epoll管理下的read过程
解析: eventpoll(操作系统支持):socket事件发生时,构建一个event事件列表,指明哪些sokcet上有事件到达。
遍历事件列表,就可以找到对应的socket。
selector不用遍历所有的sokcet,只需要遍历eventpoll,就可以找到对应的socket。
====>效率比较高。
多路复用网络通信: 多个channel复用一个selector.
11.无活动连接时,Selector.select方法被阻塞
评论