nio是java的IO框架里边十分重要的一部分内容,其最核心的就是提供了非阻塞IO的处理方式,最典型的应用场景就是处理网络连接。很多同学提起nio都能说起一二,但是细究其背后的原理、思想往往就开始背书,说来说去都是那么几句,其中不少人并不见的真的很理解。本人之前就属于此类,看了很多书和博客,但是大多数都只是讲了三件套和怎么使用,很少会很细致的讲背后的思想,那本次我们就来扒一扒吧。
很多博客描述nio都是这么说的:基于Reactor模式实现的多路非阻塞高性能的网络IO。那么我们就从这个定义来分析,其中两个关键点:多路非阻塞和Reactor模式。(本来想把高性能也算进去,但是后来想想这个应该算前两者的结果)下边我们来分别搞懂这两块。
多路非阻塞其实准确的名字叫做IO多路复用模型,其是linux五种网络模型之一,也是当前网络编程最常使用的模型之一。至于详细的介绍请参考博客:高性能IO模型浅析(这个里边只给出了4中,没有信号驱动IO,但讲的很赞,特别是图),这里仅作简要介绍和对比:
对比以上五种模型可以知道,IO复用模型从效率和实现成本综合而言目前是比较好的选择,这就是java基于该模型实现nio的根本原因。上边提到了IO复用模型的实现思想,其实这种思想在其他语言中早已实现(如C++中据说流弊哄哄超10w行代码的ACE,自适配通信环境,就采用了该模型),并且提出了一个叫Reactor的设计模式。
Reactor模式,翻译过来叫做反引器模式,其目的是在事件驱动的应用中,将一个请求的能够分离并且调度给应用程序。我相信大多数人都没看明白前一句的意思(书还是要背的),说白了就是对于一个请求的多个事件(如连接、读写等),经过这种模式的处理,能够区分出来,并且分别交给对应的处理模块处理。废话不多说,来看下一个简图:
再来看个简图吧:
基本上和Reactor能对应上,少了个dispatcher,这是由于jdk本身提供的nio比较基本,dispatcher一般都由我们自己实现,而在我理解中,mina、netty这些框架很重要的一方面也是提供了该部分的实现。
从《netty权威指南》上抄了个例子以及配图,而且代码没有客户端的,大家可以瞄一眼吧(为什么没有?因为已经快一点了,我不想写了......):
服务器端时序图:
客户端时序图:
服务器端代码:
package com.gj.netty.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.util.Iterator;import java.util.Set;/** * Created by guojing on 2015/6/7. */public class MultiplexerTimerServer implements Runnable { PRivate Selector selector; private ServerSocketChannel servChannel; private volatile boolean stop; public MultiplexerTimerServer(int port) { try { selector = Selector.open(); //新建多路复用selector servChannel = ServerSocketChannel.open(); //新建channel servChannel.configureBlocking(false); //设置非阻塞 servChannel.socket().bind(new InetSocketAddress(port),1024); //端口、块大小 servChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("TimeServer is start, port:" + port); } catch (IOException e) { e.printStackTrace(); } } public void run() { while (!stop){ try { selector.select(1000); Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> ketIt = keys.iterator(); SelectionKey key = null; while (ketIt.hasNext()){ key = ketIt.next(); ketIt.remove(); //处理对应key事件 handler(key); } } catch (IOException e) { e.printStackTrace(); } } } private void handler(SelectionKey key){ //根据key去除channel做对应处理 }}
我想如果这会儿还有人记得标题一定会骂我了,丫的十分钟个屁啊,认真看完至少待半个小时。这个我只能说如果你之前已经理解了,那么画个10分钟瞟一眼无所谓的,如果以前没理解,如果本文能让你有了更好的理解,那么花多少时间更无所谓了,要知道懂了java的nio是量的积累,了解了其背后的思想和原理是质的积累。而且,我明明计划半小时写完的,这会已经2个多小时过去了......
新闻热点
疑难解答