当前位置:网站首页>BIO,NIO,AIO实践学习笔记(便于理解理论)
BIO,NIO,AIO实践学习笔记(便于理解理论)
2022-08-05 05:24:00 【monkeyhlj】
BIO
BIO: 同步并阻塞 ,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。它的有点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。
阻塞I/O 在调用一个io函数的时候,如果没有获取到数据的情况下,那么就会一直等待;等待的过程中会导致整个应用程序一直是一个阻塞的过程,无法去做其他的实现。
package com.monkey.bio;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketServer {
public static void main(String[] args) throws IOException{
//cmd命令: telnet localhost 9000
//help
//send xiaoxi
ServerSocket serverSocket = new ServerSocket(9000);
while (true){
System.out.println("等待连接。。。");
//阻塞方法
Socket clientSocket = serverSocket.accept();
System.out.println("有客户端连接了。。。");
//优化--多线程
//缺点:不能支持大量连接(C10m问题导致服务器宕机)
//改进:线程池,但是如果都在handler中等待读数据时阻塞,那么这个服务器还是不能处理新的客户端连接
new Thread(new Runnable() {
@Override
public void run() {
try {
handler(clientSocket);
}catch (IOException e){
e.printStackTrace();
}
}
}).start();
// handler(clientSocket);
}
}
private static void handler(Socket clientSocket) throws IOException{
byte[] bytes = new byte[1024];
System.out.println("准备read。。。");
//接收客户端的数据,阻塞方法,没有数据可读时就阻塞
int read = clientSocket.getInputStream().read(bytes);
System.out.println("read完毕。。");
if (read != -1) {
System.out.println("接收到客户端的数据:"+ new String(bytes,0,read));
}
System.out.println("end");
//clientSocket.getOutputStream().write("HelloCLient".getBytes());
//clientSocket.getOutputStream().flush();
}
}
/* Redis,Zookeeper,Netty,游戏服务器等其实底层就是IO通信程序 */
NIO
NIO: 同步非阻塞 ,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/0请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
非阻塞I/O 不管是否有获取到数据,都会立马获取结果,如果没有获取数据的话、那么就不间断的循环重试,但是我们整个应用程序不会实现阻塞。
原始的NIO:
package com.monkey.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class NioServer {
//保存客户端连接
static List<SocketChannel> channelList = new ArrayList<>();
public static void main(String[] args) throws IOException,InterruptedException{
//创建NIO ServerSocketChannel, 与BIO的serverSocket类似
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(9000));
//设置ServerSocketChanne为非阻塞
serverSocket.configureBlocking(false);
System.out.println("服务启动成功");
while(true){
//非阻塞模式accept方法不会阻塞,否则会阻塞
//NIO的非阻塞是由操作系统内部实现的,底层调用了Linux内核的accept函数
SocketChannel socketChannel = serverSocket.accept();
if(socketChannel != null){
//如果有客户端连接
System.out.println("连接成功");
//设置SocketChanneL为非阻塞
socketChannel.configureBlocking(false);
//保存客户端连接在List中
channelList.add(socketChannel);
}
//遍历连接进行数据读取
Iterator<SocketChannel> iterator = channelList.iterator();
while (iterator.hasNext()){
SocketChannel sc = iterator.next();
ByteBuffer byteBuffer = ByteBuffer.allocate(6);
//非阻塞模式read方法不会阻塞,否则会阻塞
int len = sc.read(byteBuffer);
//如果有数据。把数据打印出来
if(len > 0){
System.out.println("接收到消息:"+new String(byteBuffer.array()));
}else if(len == -1){
//如果客户端断开,把socket从集合中去掉
iterator.remove();
System.out.println("客户端连接断开");
}
}
}
}
}
//缺点:1、一个连接的时间不能太长,否则会导致后续连接阻塞
//2、连接数量过多,时间复杂度会过高
//3、连接没有消息时也会加入遍历,无效遍历,浪费资源
//优化:多路复用器
改进:使用多路复用器
如上图:
package com.monkey.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NioSelectorServer {
public static void main(String[] args) throws IOException,InterruptedException{
//创建NIO ServerSocketChannel, 与BIO的serverSocket类似
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9000));
//设置ServerSocketChanne为非阻塞
serverSocketChannel.configureBlocking(false);
//打开Selector处理Channel,即创建epoll
Selector selector = Selector.open();
//把ServerSocketChanneL注册到seLector上,并且seLector对客厂7端accept连接操作感兴趣
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务启动成功");
while (true){
//阻塞等待需要处理的事件发生(即有消息来时才跳出阻塞)
selector.select();
//获取seLector中注册的全部事件的SelectionKey实例
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
//遍历SeLectionKey对事件进行处理
while (iterator.hasNext()){
SelectionKey key = iterator.next();
//如果是0P_ ACCEPT事件, 则进行连接获取和事件注册
if(key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
//这里只注册了读事件,如果需要给客户端发送数据可以注册写事件
socketChannel.register(selector,SelectionKey.OP_READ);
System.out.println("客户端连接成功");
}else if(key.isReadable()){
//如果是0P_ READ 事件,则进行读取和打印
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(6);
int len = socketChannel.read(byteBuffer);
//如果有数据。把数据打印出来
if(len > 0){
System.out.println("接收到消息:"+new String(byteBuffer.array()));
}else if(len == -1){
//如果客户端断开,关闭socket
System.out.println("客户端连接断开");
socketChannel.close();
}
}
//从事件集合里删除本次处理的key,防止下次seLect重复处理
iterator.remove();
}
}
}
}
其底层实现–epoll
I/O多路复用模型能处理多个connection的优点就使其能支持更多的并发连接请求。
select的几大缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
(3)select支持的文件描述符数量太小了,32位系统1024,64位系统2048
poll的实现和select非常相似,只是描述fd集合的方式不同,poll使用pollfd结构(链表结构)而不是select的fd_set结构,所以连接数上没有限制,其他的都差不多。
epoll既然是对select和poll的改进,就应该能避免上述的三个缺点。那epoll都是怎么解决的呢?
- 对于第一个缺点,epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次。
- 对于第二个缺点,epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果)。
- 对于第三个缺点,epoll没有这个限制,它所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/filemax察看,一般来说这个数目和系统内存关系很大。
epoll是Linux目前大规模网络并发程序开发的首选模型。在绝大多数情况下性能远超select和poll。目前流行的高性能web服务器Nginx正式依赖于epoll提供的高效网络套接字轮询服务。但是,在并发连接不高的情况下,多线程+阻塞I/O方式可能性能更好。
AIO
AIO: 异步非阻塞 ,服务器实现模式为一个有效请求一个线程,客户端的/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。AIO方式使用于连接数目多且连接比较长(重操作〉的架构,比如相册服务器,充分调用Os参与并发操作,编程比较复杂,JDK7开始支持。
【参考】https://www.bilibili.com/video/BV1Am4y1f7Ec?p=3&spm_id_from=333.880.my_history.page.click
边栏推荐
- What's the point of monitoring the involution of the system?
- This is indeed the best article on microservice architecture I have read!
- 路由器和静态路由的配置
- The problem come from line screening process
- The highlight moment of operation and maintenance starts with intelligence
- Met with the browser page
- Spark source code - task submission process - 6-sparkContext initialization
- 技术分享杂七杂八技术
- wc, grep, tar, vi/vim
- 程序员应该这样理解I/O
猜你喜欢
正则表达式小实例--验证邮箱地址
Technology Sharing Miscellaneous Technologies
Mina的长连接和短连接
Complete mysql offline installation in 5 minutes
In-depth Zabbix user guide - from the green boy
Mongodb查询分析器解析
Hugo builds a personal blog
深度 Zabbix 使用指南——来自惨绿少年
Transport layer protocol (TCP 3-way handshake)
Passing parameters in multiple threads
随机推荐
time complexity and space complexity
Advantages of overseas servers
selenium模块的操作之拉钩
初识网页与浏览器
跨域的十种解决方案详解(总结)
TCP/IP four-layer model
Problems encountered in installing Yolo3 target detection module in Autoware
Billions of IT operations in the market, the product by strength to speak
运维的高光时刻,从智能化开始
By solving these three problems, the operation and maintenance efficiency will exceed 90% of the hospital
Met with the browser page
原生JS带你了解数组方法实现及使用
正则表达式小实例--去掉字符串中间和两边的空格
Quick question and quick answer - FAQ of Tencent Cloud Server
Small example of regular expression--validate email address
config.js相关配置汇总
[ingress]-ingress使用tcp端口暴露服务
el-autocomplete use
逻辑卷创建
LinkSLA insists that users come first and creates a sustainable operation and maintenance service plan