谈谈传统BIO网络编程模型的局限性与NIO

2019/04/25

谈谈传统BIO网络编程模型的局限性与NIO

先来看看我们的server端:

创建一个serversocket,进行监听,每来一个客户端,就启动一个新启动为其服务:

private void createListenSocket() {
//如果创建监听socket的时候发生异常,将会隔WAIT_TIME毫秒重试,直到成功
while (true){
try {
serverSocket = new ServerSocket(LISTEN_PORT);
Log.info("创建监听socket成功,正在监听...");
break;
} catch (IOException e) {
Log.error("创建监听socket失败,原因:"+e.getMessage()+WAIT_TIME+"ms后重建");
ThreadUtils.sleep(WAIT_TIME);
}
}
}
private void listen() throws IOException {
Socket socket = null;
//启动一个死循环来监听连接本服务器的socket
while ((socket = server.accept()) != null) {
// 来访客户记录到服务器:
Destination destination = new Destination();
destination.setSocket(socket);
server.newDestination(destination);
//启动一条子线程来处理连接本服务器的socket消息收发
new SubListenThread(destination,server).start();

}
}

以上代码来自我半年前编写的一个远控程序服务端。在这个模型当中,服务端线程与客户端线程是1:1的关系,也就是说有多少个客户端,服务端就得创建多少个线程。

在java当中,创建线程的代价是很大的。如果有几千个客户端,这个程序是撑不住的。

那么像旺旺这种在线量可以达到几百万的应用,在单个机器上,势必要用一种比BIO更好的编程模型来实现。

那么NIO就可以解决这种应用场景。

关于NIO的教程,网上有非常多的详细教程,在这里就不赘述。就简单说说NIO编程模型:

首先,我们有几个基本概念:

1.channel 大家可以把它想象成stream,只不过我们可以通过channel进行双工通信,也就是能读又能写。

2.buffer 可以看做是一段缓存,我们能通过这么一段缓存对channel进行读写。

3.selector 中文直接翻译过来就是选择器,因为在NIO当中,我们不通过传统的一个线程处理一个客户端,而是通过轮询的方式知道哪些客户端可读、可连接,从而实现1:n的模型。

那么NIO的编程模型如下:

通过一个selector,客户端会连接到这个selector,我们可以通过一个死循环不断地轮询selector有哪些客户端,然后一旦监听到可读或者可连接事件,我们就进行相应的业务逻辑处理,该读读,该写写。

以下为一个简单多人聊天室部分源码:



看起来还是很复杂的,至少比传统的BIO通信模型复杂。

使用原生的nio api进行编程很难,而且这玩意还有bug,据说在1.8以上版本仍存在空轮询BUG,会让CPU飙到100%。

所以说,如果可以的话,尽量使用一些第三方框架,比如netty。

Post Directory