Netty是什么
它是基于NIO的再度封装,修复了JDK 原有NIO的诟病,而近乎完美的网络通信框架。
那什么是NIO?
关于这个问题,我们可以先从 Java 最原始的 BIO说起。
首先强调一下,对 Java基本网络编程 的理解很重要,因为这是一层套一层的关系。如图:
这是个逐层封装和优化的过程。其中的核心主体一直都是网络。
我们这里先重点说一下BIO,也就是我们的传统Java网络编程。
Java网络编程(BIO)
我们首先给出结论,稍后我们代码验证。
如图:
当有一个客户端向服务器发出请求时,服务器便创建一个线程与之通信。
这种策略显然有个很大的限制,就是无法招架高并发,,
当并发请求多了,服务器就招架不住了,,可能回绝后续的请求,也可能崩溃。
Code
服务器端:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@SuppressWarnings("all")
public class BIOServer {
public static void main(String[] args) throws IOException {
//线程池机制
/** 思路:
* 1.创建一个线程池
* 2.如果有客户端接入,创建一个线程与之通信
*/
//线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
ServerSocket server = new ServerSocket(6666);
System.out.println("服务启动了");
while (true){
//创建一个Socket
final Socket socket = server.accept();
System.out.println("接入一个客户端——线程信息:"+Thread.currentThread().getName());
//创建一下线程与之通信
threadPool.execute(()->{
handler(socket);
});
}
}
public static void handler(Socket socket) {
try {
InputStream inputStream = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len;
while (true){
len = inputStream.read(bytes);
if (len != 0){
baos.write(bytes,0,len);
System.out.println("线程信息:" + Thread.currentThread().getName()+":“"+baos.toString()+"”");
}
else break;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
System.out.println("关闭连接——对应线程:"+Thread.currentThread().getName());
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端我们采用Windows的Telnet:
验证流程:
服务器跑起来,但他在被阻塞着,因为他在等一个请求。
当我们执行完 “telnet 127.0.0.1 6666” 这句命令时,,
由主线程为我们负责接入客户端,然后线程又阻塞了,因为他在等待客户端发消息来。
服务器端打印结果:(收到了来自客户端的消息,并开始等待下次消息)
此时负责与该客户端通信的线程是 pool-1-thread-1
接下来我们在来一个客户端:
再开一个cmd窗口,并执行 telnet 127.0.0.1 6666 这条命令。
服务器打印结果:
可见,主线程又接入了一个客户端。
我们尝试通讯:
服务器端打印结果:
可见,这次负责和第二个客户端通信的线程为:pool-1-thread-2 。
由此得出,BIO 确实是每个客户端对应给出一个线程。
马师傅:这样好吗? 这样不好。。
很显然,这样确实不好。局限性太大。
BIO是在JDK1.4之前唯一的方法,从JDK1.4之后 开始支持 NIO。
NIO
NIO 是技术的发展,所以 他本质上就是优化 BIO。
先上图:
在听我解释:
之前的BIO是一个客户端对应一个线程,会有一个比较严重的问题:
- 当客户端和服务器之间没有通信时,这个线程就会在这闲着。(这无疑是浪费)
所以有了NIO,来降低资源的闲置率。
上述模型主要阐述的思想就是,一个线程对一个客户端可能会有较多的时间处于闲置状态,所以我们就让一个线程多负责几个客户端,加一个 选择器(selector) 来 轮询 他下面的客户端请求,谁有请求数据就让线程服务谁。
这无疑大大提高了资源利用率,并且也大大增加了并发量。
最后总结一下 BIO 和 NIO 的特点:
-
Java BIO : 同步阻塞,服务器实现模式为一个客户端一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
阻塞:客户端会等着请求完成才能去做其他事。
-
Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器(selector)上,多路复用器(selector)轮询到连接有I/O请求时才让线程进行处理。
非阻塞:客户端不用等着请求完成 ,期间可以去其他事,但要时不时的去询问一下服务器的请求执行情况。