十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
这篇文章主要介绍“什么是Nginx、BIO、NIO、AIO”,在日常操作中,相信很多人在什么是Nginx、BIO、NIO、AIO问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”什么是Nginx、BIO、NIO、AIO”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
成都创新互联公司长期为上千家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为公主岭企业提供专业的成都网站设计、网站制作,公主岭网站改版等技术服务。拥有十多年丰富建站经验和众多成功案例,为您定制开发。
同步,一个任务的完成之前不能做其他操作,必须等待(等于在打电话)
异步,一个任务的完成之前,可以进行其他操作(等于在聊QQ)
阻塞,是相对于CPU来说的, 挂起当前线程,不能做其他操作只能等待
非阻塞,,无须挂起当前线程,可以去执行其他操作
BIO:同步并阻塞,服务器实现一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,没处理完之前此线程不能做其他操作(如果是单线程的情况下,我传输的文件很大呢?),当然可以通过线程池机制改善。BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO:同步非阻塞,服务器实现一个连接一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4之后开始支持。
AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由操作系统先完成了再通知服务器应用去启动线程进行处理,AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用操作系统参与并发操作,编程比较复杂,JDK1.7之后开始支持。.
AIO属于NIO包中的类实现,其实IO主要分为BIO和NIO,AIO只是附加品,解决IO不能异步的实现
在以前很少有Linux系统支持AIO,Windows的IOCP就是该AIO模型。但是现在的服务器一般都是支持AIO操作
BIO是阻塞的,NIO是非阻塞的.
BIO是面向流的,只能单向读写,NIO是面向缓冲的, 可以双向读写
使用BIO做Socket连接时,由于单向读写,当没有数据时,会挂起当前线程,阻塞等待,为防止影响其它连接,,需要为每个连接新建线程处理.,然而系统资源是有限的,,不能过多的新建线程,线程过多带来线程上下文的切换,从来带来更大的性能损耗,因此需要使用NIO进行BIO多路复用,使用一个线程来监听所有Socket连接,使用本线程或者其他线程处理连接
AIO是非阻塞 以异步方式发起 I/O 操作。当 I/O 操作进行时可以去做其他操作,由操作系统内核空间提醒IO操作已完成(不懂的可以往下看)
注意:我这里的用户空间就是应用程序空间
B也在河边钓鱼,但是B不想将自己的所有时间都花费在钓鱼上,在等鱼上钩这个时间段中,B也在做其他的事情(一会看看书,一会读读报纸,一会又去看其他人的钓鱼等),但B在做这些事情的时候,每隔一个固定的时间检查鱼是否上钩。一旦检查到有鱼上钩,就停下手中的事情,把鱼钓上来。 B在检查鱼竿是否有鱼,是一个轮询的过程。
G也在河边钓鱼,但与A、B、C不同的是,G比较聪明,他给鱼竿上挂一个铃铛,当有鱼上钩的时候,这个铃铛就会被碰响,G就会将鱼钓上来。
Bit最小的二进制单位 ,是计算机的操作部分取值0或者1
Byte是计算机中存储数据的单元,是一个8位的二进制数,(计算机内部,一个字节可表示一个英文字母,两个字节可表示一个汉字。) 取值(-128-127)
Char是用户的可读写的最小单位,他只是抽象意义上的一个符号。如‘5’,‘中’,‘¥’ 等等等等。在java里面由16位bit组成Char 取值 (0-65535)
Bit 是最小单位 计算机他只能认识0或者1
Byte是8个字节 是给计算机看的
字符 是看到的东西 一个字符=二个字节
对象序列化,将对象以二进制的形式保存在硬盘上
反序列化;将二进制的文件转化为对象读取
实现serializable接口,不想让字段放在硬盘上就加transient
如果用户没有自己声明一个serialVersionUID,接口会默认生成一个serialVersionUID
但是强烈建议用户自定义一个serialVersionUID,因为默认的serialVersinUID对于class的细节非常敏感,反序列化时可能会导致InvalidClassException这个异常。
(比如说先进行序列化,然后在反序列化之前修改了类,那么就会报错。因为修改了类,对应的SerialversionUID也变化了,而序列化和反序列化就是通过对比其SerialversionUID来进行的,一旦SerialversionUID不匹配,反序列化就无法成功。
属于处理流中的缓冲流,可以将读取的内容存在内存里面,有readLine()方法
超类代表顶端的父类(都是抽象类)
java.io.InputStream
java.io.OutputStream
java.io.Reader
java.io.Writer
这里的基本操作就是普通的读取操作,如果想要跟深入的了解不同的IO开发场景必须先了解IO的基本操作
我这使用Socket简单的来模拟网络编程IO会带来的问题
不懂Socket可以看我之前的文章,这个东西很容易懂的,就是基于TCP实现的网络通信,比http要快,很多实现网络通信的框架都是基于Socket来实现
package com.test.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; //TCP协议Socket使用BIO进行通信:服务端 public class BIOServer { // 在main线程中执行下面这些代码 public static void main(String[] args) { //使用Socket进行网络通信 ServerSocket server = null; Socket socket = null; //基于字节流 InputStream in = null; OutputStream out = null; try { server = new ServerSocket(8000); System.out.println("服务端启动成功,监听端口为8000,等待客户端连接..."); while (true){ socket = server.accept(); //等待客户端连接 System.out.println("客户连接成功,客户信息为:" + socket.getRemoteSocketAddress()); in = socket.getInputStream(); byte[] buffer = new byte[1024]; int len = 0; //读取客户端的数据 while ((len = in.read(buffer)) > 0) { System.out.println(new String(buffer, 0, len)); } //向客户端写数据 out = socket.getOutputStream(); out.write("hello!".getBytes()); } } catch (IOException e) { e.printStackTrace(); } } } TCP协议Socket使用BIO进行通信:客户端(第二执行) package com.test.io; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; //TCP协议Socket使用BIO进行通信:客户端 public class Client01 { public static void main(String[] args) throws IOException { //创建套接字对象socket并封装ip与port Socket socket = new Socket("127.0.0.1", 8000); //根据创建的socket对象获得一个输出流 //基于字节流 OutputStream outputStream = socket.getOutputStream(); //控制台输入以IO的形式发送到服务器 System.out.println("TCP连接成功 \n请输入:"); String str = new Scanner(System.in).nextLine(); byte[] car = str.getBytes(); outputStream.write(car); System.out.println("TCP协议的Socket发送成功"); //刷新缓冲区 outputStream.flush(); //关闭连接 socket.close(); } } package com.test.io; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; //TCP协议Socket:客户端 public class Client02 { public static void main(String[] args) throws IOException { //创建套接字对象socket并封装ip与port Socket socket = new Socket("127.0.0.1", 8000); //根据创建的socket对象获得一个输出流 //基于字节流 OutputStream outputStream = socket.getOutputStream(); //控制台输入以IO的形式发送到服务器 System.out.println("TCP连接成功 \n请输入:"); String str = new Scanner(System.in).nextLine(); byte[] car = str.getBytes(); outputStream.write(car); System.out.println("TCP协议的Socket发送成功"); //刷新缓冲区 outputStream.flush(); //关闭连接 socket.close(); } }
为了解决堵塞问题,可以使用多线程,请看下面
这时有人就会说,我多线程不就解决了吗?
使用多线程是可以解决堵塞等待时间很长的问题,因为他可以充分发挥CPU
然而系统资源是有限的,不能过多的新建线程,线程过多带来线程上下文的切换,从来带来更大的性能损耗
package com.test.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; //TCP协议Socket使用多线程BIO进行通行:服务端 public class BIOThreadService { public static void main(String[] args) { try { ServerSocket server = new ServerSocket(8000); System.out.println("服务端启动成功,监听端口为8000,等待客户端连接... "); while (true) { Socket socket = server.accept();//等待客户连接 System.out.println("客户连接成功,客户信息为:" + socket.getRemoteSocketAddress()); //针对每个连接创建一个线程, 去处理I0操作 //创建多线程创建开始 Thread thread = new Thread(new Runnable() { public void run() { try { InputStream in = socket.getInputStream(); byte[] buffer = new byte[1024]; int len = 0; //读取客户端的数据 while ((len = in.read(buffer)) > 0) { System.out.println(new String(buffer, 0, len)); } //向客户端写数据 OutputStream out = socket.getOutputStream(); out.write("hello".getBytes()); } catch (IOException e) { e.printStackTrace(); } } }); thread.start(); } } catch (IOException e) { e.printStackTrace(); } } }
为了解决线程太多,这时又来了,线程池
package com.test.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //TCP协议Socket使用线程池BIO进行通行:服务端 public class BIOThreadPoolService { public static void main(String[] args) { //创建线程池 ExecutorService executorService = Executors.newFixedThreadPool(30); try { ServerSocket server = new ServerSocket(8000); System.out.println("服务端启动成功,监听端口为8000,等待客户端连接..."); while (true) { Socket socket = server.accept(); //等待客户连接 System.out.println("客户连接成功,客户信息为:" + socket.getRemoteSocketAddress()); //使用线程池中的线程去执行每个对应的任务 executorService.execute(new Thread(new Runnable() { public void run() { try { InputStream in = socket.getInputStream(); byte[] buffer = new byte[1024]; int len = 0; //读取客户端的数据 while ((len = in.read(buffer)) > 0) { System.out.println(new String(buffer, 0, len)); } //向客户端写数据 OutputStream out = socket.getOutputStream(); out.write("hello".getBytes()); } catch (IOException e) { e.printStackTrace(); } } } ) ); } } catch (IOException e) { e.printStackTrace(); } } }
package com.test.io; import com.lijie.iob.RequestHandler; 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 NIOServer { public static void main(String[] args) throws IOException { //111111111 //Service端的Channel,监听端口的 ServerSocketChannel serverChannel = ServerSocketChannel.open(); //设置为非阻塞 serverChannel.configureBlocking(false); //nio的api规定这样赋值端口 serverChannel.bind(new InetSocketAddress(8000)); //显示Channel是否已经启动成功,包括绑定在哪个地址上 System.out.println("服务端启动成功,监听端口为8000,等待客户端连接..."+ serverChannel.getLocalAddress()); //22222222 //声明selector选择器 Selector selector = Selector.open(); //这句话的含义,是把selector注册到Channel上面, //每个客户端来了之后,就把客户端注册到Selector选择器上,默认状态是Accepted serverChannel.register(selector, SelectionKey.OP_ACCEPT); //33333333 //创建buffer缓冲区,声明大小是1024,底层使用数组来实现的 ByteBuffer buffer = ByteBuffer.allocate(1024); RequestHandler requestHandler = new RequestHandler(); //444444444 //轮询,服务端不断轮询,等待客户端的连接 //如果有客户端轮询上来就取出对应的Channel,没有就一直轮询 while (true) { int select = selector.select(); if (select == 0) { continue; } //有可能有很多,使用Set保存Channel SetselectionKeys = selector.selectedKeys(); Iterator iterator = selectionKeys.iterator(); while (iterator.hasNext()) { //使用SelectionKey来获取连接了客户端和服务端的Channel SelectionKey key = iterator.next(); //判断SelectionKey中的Channel状态如何,如果是OP_ACCEPT就进入 if (key.isAcceptable()) { //从判断SelectionKey中取出Channel ServerSocketChannel channel = (ServerSocketChannel) key.channel(); //拿到对应客户端的Channel SocketChannel clientChannel = channel.accept(); //把客户端的Channel打印出来 System.out.println("客户端通道信息打印:" + clientChannel.getRemoteAddress()); //设置客户端的Channel设置为非阻塞 clientChannel.configureBlocking(false); //操作完了改变SelectionKey中的Channel的状态OP_READ clientChannel.register(selector, SelectionKey.OP_READ); } //到此轮训到的时候,发现状态是read,开始进行数据交互 if (key.isReadable()) { //以buffer作为数据桥梁 SocketChannel channel = (SocketChannel) key.channel(); //数据要想读要先写,必须先读取到buffer里面进行操作 channel.read(buffer); //进行读取 String request = new String(buffer.array()).trim(); buffer.clear(); //进行打印buffer中的数据 System.out.println(String.format("客户端发来的消息: %s : %s", channel.getRemoteAddress(), request)); //要返回数据的话也要先返回buffer里面进行返回 String response = requestHandler.handle(request); //然后返回出去 channel.write(ByteBuffer.wrap(response.getBytes())); } iterator.remove(); } } } }
package com.test.io; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; //TCP协议Socket:客户端 public class Client01 { public static void main(String[] args) throws IOException { //创建套接字对象socket并封装ip与port Socket socket = new Socket("127.0.0.1", 8000); //根据创建的socket对象获得一个输出流 OutputStream outputStream = socket.getOutputStream(); //控制台输入以IO的形式发送到服务器 System.out.println("TCP连接成功 n请输入:"); while(true){ byte[] car = new Scanner(System.in).nextLine().getBytes(); outputStream.write(car); System.out.println("TCP协议的Socket发送成功"); //刷新缓冲区 outputStream.flush(); } } }
Netty是由JBOSS提供的一个Java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的Socket服务开发。
到此,关于“什么是Nginx、BIO、NIO、AIO”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注创新互联网站,小编会继续努力为大家带来更多实用的文章!