其实我的重点是和大家一起学习Netty下一章。但是,这个Netty不适合直接对话。为什么,我们学习一门技术一定要知道技术的初衷,对吧?下面我给大家简单介绍一下什么是Netty。Netty是一个异步事件驱动的网络应用框架,用于快速开发高性能、高可靠的网络服务器和客户端程序。Netty简化了网络程序的开发,是BIO、NIO、AIO演进的产物,属于NIO框架。在我们平时使用的众多中间件中,Netty用于很多底层通信,比如rocketmq、dubbo。我们最常用的底层通信是netty,足以说明这个性能有多么优秀。好了,我们先了解一下同步、异步、阻塞、非阻塞这四个概念。从简单入手,我们以经典的读取文件模型为例。(对于操作系统来说,所有的输入输出设备都被抽象成文件。)应用层在发起读取文件的请求时,会调用系统内核的I/O接口。阻塞与非阻塞如果应用层调用阻塞I/O,那么调用之后,应用层立即挂起,等待数据返回,直到系统内核从磁盘读取数据返回给应用层,应用层利用获得的数据进行其他操作。如果应用层调用非阻塞I/O,那么调用之后,系统内核会立即返回(虽然没有文件内容的数据),应用层不会被挂起,可以做其他任何操作它想做。(至于文件内容数据如何返回给应用层,这已经超出了阻塞和非阻塞的区别。)这就是阻塞和非阻塞(脱离了同步和异步之后)的区别。总结一下,不管是阻塞还是非阻塞,重点都在接口调用(发送请求)后等待数据返回时的状态。那些被挂起而无法执行其他操作的是阻塞的,那些可以立即“抽出”来完成其他“任务”的是非阻塞的。同步异步阻塞和非阻塞解决了应用层等待数据返回时的状态问题,那么系统内核??获取到的数据如何返回给应用层呢?这里的不同类型的操作体现了同步和异步的区别。对于同步调用,应用层需要自行查询系统内核。如果还没有读取数据,说明此时读取文件的任务还没有完成。应用层会根据文件的阻塞和非阻塞划分来阻塞或挂起文件。或者做其他事情(所以同步和异步不判断等待数据返回时的状态);如果数据已经被读取,此时系统内核会将数据返回给应用层,应用层可以使用获取到的数据做其他相关的事情。对于异步调用,应用层不需要主动查询系统内核。系统内核读取文件数据后,会主动通知应用层数据已读取。此时应用层可以接收到系统内核返回的数据。数据,并做其他事情。这就是同步和异步的区别(脱离了阻塞和非阻塞之后)。也就是说,无论是同步还是异步,重点都在于任务完成时消息的通知方式。调用者盲目主动查询的方法是同步调用,主动通知调用者任务已经被调用者完成的方法是异步调用。您必须了解上述概念。这是基础。必须了解以上内容,才能真正了解netty的源码。这也是面试中经常被问到的要点之一。总结一下阻塞和非阻塞,重点是发起请求后等待数据返回的状态。那些被挂起而不能执行其他操作的是阻塞类型,那些可以立即执行其他操作的是非阻塞类型。同步和异步,重点是任务完成时消息通知的方法。调用方主动查询的方法是同步调用,被叫方主动通知调用方任务已经完成的方法是异步调用。网上最常见的例子之一就是开水的例子,我继续给大家啰嗦一下。老王烧水,老王把水放在炉子上,等在这里,什么都不做,需要随时查看水是否烧开,这叫阻塞同步,阻塞的原因是老王不能'tdoanythingDoit,同步,因为水开了,他得自己看着。老王后来学乖了,就不在这里等了。把水放在炉子上,他就去玩了一场紧张刺激的lol手游。这称为非阻塞同步。非阻塞是因为老王在等水的时候,自己在玩游戏,但是因为水已经烧开了,他还得看同步。后来老王觉得自己看水太麻烦,就买了升级版的水壶,太神奇了。水烧开后,这个水壶会发出嘶嘶声,嘿嘿。老王不用每隔几分钟检查水是否烧开,只需要听一声哨子,烧水的时候就可以玩游戏,水烧开他会主动通知老王,这就是异步非阻塞,非阻塞是因为老王会玩游戏,异步是水壶的汽笛声。现在大家应该很好理解了吧!接下来继续看BIO、NIO、AIOSocket网络通信过程简单分为以下4个步骤:建立服务器,监听客户端请求。客户端请求,服务器与客户端建立连接。数据可以在两端之间传递。关闭资源。传统阻塞式通信BIO流程BIO是最传统的阻塞式同步通信方式,也是最简单的一种。使用起来比较方便,但是处理并发度低,通信费时。服务端会通过一个线程监听客户端的请求,并为每个客户端创建一个新的线程来处理链接,这是典型的请求-响应模式。如果客户端数量增加,需要频繁创建和销毁线程,会给服务器带来很大压力。服务器提供IP地址和侦听端口。客户端通过三次TCP握手与服务器建立连接。连接成功后,双方通过,然后挥手四次断开连接。即使使用线程池来完善新增的线程,这也是一种伪异步IO,这样实现可以为少量客户端提供服务。如果客户端并发足够,还是会因为线程池满了而导致OOM问题。.让我给你展示一个简单的恶魔publicclassSocketServer{publicstaticvoidmain(String[]args)throwsIOException{socketServer.start(9000);}publicvoidstart(intport){//1.创建一个ServerSocket对象并绑定一个端口try(ServerSocketserver=newServerSocket(port);){System.out.println("serverstart");插座插座;//2。通过accept()方法监听客户端请求,该方法将阻塞直到连接建立while((socket=server.accept())!=null){System.out.println("clientconnected");try(ObjectInputStreamobjectInputStream=newObjectInputStream(socket.getInputStream());ObjectOutputStreamobjectOutputStream=newObjectOutputStream(socket.getOutputStream())){//3.通过输入流读取客户端发送的请求信息Stringmessage=(String)objectInputStream.readObject();System.out.println("服务器接收消息ge:"+message);//4.通过输出流向客户端发送响应信息objectOutputStream.writeObject(message);objectOutputStream.flush();}catch(IOException|ClassNotFoundExceptione){System.out.println("发生异常:");}}}catch(IOExceptione){System.out.println("occurIOException:");}}}这是服务器代码:publicclassClient{publicObjectsend(Stringmessage,Stringhost,intport){//1.创建一个Socket对象并指定服务器的地址和端口号//2.通过输出流向服务器发送请求信息objectOutputStream.writeObject(message);//3.通过输入流获取服务器响应信息ObjectInputStreamobjectInputStream=newObjectInputStream(socket.getInputStream());读取对象ct();}catch(ClassNotFoundException|IOExceptione){System.out.println("发生异常:");}返回空值;}publicstaticvoidmain(String[]args){ClienthelloClient=newClient();helloClient.send("来自客户端的内容","127.0.0.1",9000);System.out.println("发送数据成功");}}这是客户端的代码,我们先运行服务端,再运行客户端,看看效果。服务器启动后,会一直阻塞在这里,等待客户端的连接处理。然后我们启动客户端,看到数据发送成功。这个时候我们切换到服务器的控制台看看效果。我们也可以通过命令行直接执行telnetlocalhost9000连接服务器,效果如下:从上面例子中看到的问题可以看出,服务器和客户端通信成功了,即这个服务器的代码只能服务一个客户端服务,当然有办法改进。我们监听连接后,立即用newThread().start()创建一个线程,用于客户端的下一步处理。这也意味着每个客户端都必须为其处理创建一个线程。如果客户端很多,或者客户端处理速度慢,那就很糟糕了。我们从线程一文中也介绍过,线程是一种非常宝贵的资源。我们需要合理使用这些资源,根据机器的性能合理控制线程数。即使线程池可以优化上面的例子,降低线程创建和销毁的成本,我们也可以实现线程池的最大数量,控制线程资源的使用。但是,即使我们改进了,也没有从根本上解决这个问题。从根本上还是属于BIO,也就是同步阻塞IO的模式。NIO同步非阻塞模型在JDK1.4中引入了NIO框架。NIO中的N可以理解为Non-blocking。NIO是面向缓冲区和基于通道的操作。NIO提供了两种不同的socket通道,ServerSocketChannel和SocketChannel,分别对应传统BIO模型中的ServerSocket和Socket,分别对应服务端和客户端。两个通道都支持阻塞和非阻塞模式。一般不使用阻塞模式。既然用了blocking,就说明和上面的BIO一样使用,性能和可靠性都不是很好。非阻塞模式对高负载、高并发的网络应用非常友好。我们后面要讲的Netty就是基于这个改进的。NIO是对BIO的一大改进。客户端和服务器之间的通信是通过Channel。NIO可以对Channel进行读写操作。这些通道将在选择器多路复用器上注册。Selector通过线程不断轮询这些Channel。找出哪个Channel准备好执行IO操作。NIO通过一个线程轮询来实现千万级客户端的请求,这是非阻塞NIO的特点。NIO核心组件Channel:与流不同,Channel是双向的。NIO可以通过Channel进行数据的读取、写入和同步读写操作。Channel分为两类:一类是网络读写(SelectableChannel),一类是文件操作(FileChannel)。我们使用的SocketChannel和ServerSocketChannel都是SelectableChannel的子类。Buffer:是NIO和BIO的重要区别。BIO是直接向Stream对象写入或读取数据。NIO的数据操作都是在缓冲区中进行的。缓冲区实际上是一个数组。选择器和选择键:多路复用器提供选择已经准备好的任务的能力。即Selector会不断轮询注册在其上的通道(Channel)。如果一个channel处于就绪状态,它会被Selector轮询,然后通过SelectionKey获取就绪的Channel集合,从而进行后续的IO操作。服务器端只要提供一个线程负责Selector的轮询,就可以访问上千个客户端。接下来我们看一下使用示例:publicclassNioServer{staticList
