Netty是一款高性能的网络通信框架,其应用也很广泛,比如常用的消息队列RocketMQ,RPC框架Dubbo在底层都有使用到Netty。在学习Netty之前,我们需要对IO模型要有一定的了解,其中最重要的就是NIO,所以今天打算先对NIO进行一些简单的梳理。
IO模型
常见IO模型分为几种:
- BIO :Blocking IO, 即同步阻塞式IO。Client和Server的每建立一次连接,都会创建一个线程,在Client等待Server响应的期间,会处于阻塞状态。
- NIO :Non-Blocking IO,即同步非阻塞式IO。NIO是基于Reactor模式,面向缓冲区并结合通道的IO模型。客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求就进行处理。
- AIO : Asynchronous IO,即异步非阻塞,采用了 Proactor 模式,特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。
既然BIO和NIO都是以同步的方式工作的,那么这里就先拿BIO与NIO做个简单的对比,比较两者的差异具体在哪些地方。
BIO
上面提到了Client和Server的每建立一次连接,都会创建一个线程并且会发生阻塞,那么我们就来简单的验证一下。验证方式也比较简单,在编辑器中创建一个ServerSocket作为服务端并给定一个端口号用于客户端连接,使用telnet作为客户端来连接服务端并实现消息发送和接收,通过代码来分析BIO会在那些地方会阻塞。
java
复制代码
public static void main(String[] args) throws IOException { // 1. 创建一个BIO服务端 端口号为9999 ServerSocket serverSocket = new ServerSocket(9998); System.out.println("=====等待客户端连接......"); // 2. 等待客户端连接 , 会阻塞 Socket socket = serverSocket.accept(); System.out.println("=====客户端已连接......"); // 3. 获取客户端发送的内容,如果客户端没有发送内容,也会阻塞 System.out.println("=====等待客户端发送数据......"); InputStream inputStream = socket.getInputStream(); while (true) { byte[] bytes = new byte[2048]; int read = inputStream.read(bytes); if (read != -1) { System.out.println((Thread.currentThread().getName() + " " + new String(bytes, 0, read))); } else { break; } } inputStream.close(); socket.close(); }
简单编码完成后,启动服务,如果在没有客户端连接的情况下,accept()
方法会阻塞,直到有客户端进行了连接。
现在可以打开cmd使用telnet命令来进行连接。连接成功后结合'Ctrl + ]'快捷键进去Telent Client,使用send命令发送数据内容。
yaml
复制代码
telnet 127.0.0.1 9998
这里可以看到,客户端虽然连接成功了,但是在调用getInputStream()
方法时,线程又被阻塞了,那么进行Telnet Client来发送数据。
一切OK,服务端收到了消息。既然说了,BIO一次连接就是一个线程,那么再发起一个客户端连接,来看看main线程到底还能不能获取到消息。
经过验证,第二个连接发送的消息,控制台确实没有收到,那就证实了一次连接就是一个线程。那有些人就会有疑问,既然控制台没打印消息,那怎么确保这条消息就被服务端接收了呢?这岂不是很简单,加个线程池,搞成伪异步不就搞定了。那么就基于上面的代码,按照下图流程方式来简单的改造改