有关nio

来源:互联网 发布:淘宝新开店铺找货源 编辑:程序博客网 时间:2024/05/11 15:49

传统的java网络编程中,是在服务端建立一个serverSocket,然后为每个客户端单独创建一个线程,分别处理各自的请求。

对已cpu而言,线程的开销是很大的,无限创建线程会让操作系统奔溃,

因此考虑到了用线程池,Tomcat就是这么搞的

可是如果高并发的情况下,情况就不乐观了,

所以开始使用nio操作,为了提高系能tomcat7也实现了nio


下面看看nio


在JDK1.4后,java引入的NIO的概念,即非阻塞的IO,服务端无需创建多个线程,仅仅只需要1个线程(将读写分别创建线程有利于提高性能)即可以处理全部客户端,解决了在性能和并发的2大问题

NIO单线程编写高性能、高并发服务器 - Seans Blog - Seans Blog

NIO采用了通道Channel选择器Selector的核心对象,Select 机制,

不用为每一个客户端连接新启线程处理,而是将其注册到特定的Selector 对象上

这就可以在单线程利用Selector 对象管理大量并发的网络连接,更好的利用了系统资源;采用非阻塞I/O 的通信方式,不要求阻塞等待I/O 操作完成即可返回,从而减少了管理I/O 连接导致的系统开销,大幅度提高了系统性能。

当有读或写等任何注册的事件发生时,可以从Selector 中获得相应的SelectionKey , 从SelectionKey 中可以找到发生的事件和该事件所发生的具体的SelectableChannel,以获得客户端发送过来的数据。由于在非阻塞网络I/O 中采用了事件触发机制,处理程序可以得到系统的主动通知,从而可以实现底层网络I/O 无阻塞、流畅地读写,而不像在原来的阻塞模式下处理程序需要不断循环等待。使用NIO,可以编写出性能更好、更易扩展的并发型服务器程序。

NIO的设计原理

       设计原理有点像设计模式中的观察者模式,由Selector去轮流咨询各个SocketChannel通道是否有事件发生,如果有,则选择出所有的Key集合,然后传递给处理程序。我们通过每个key就可以获取客户端的SocketChannel,从而进行通信。

       如果Selector发现所有通道都没有事件发生,则线程进入睡眠状态Sleep,阻塞。等到客户端有事件发生,会自动唤醒wakeup选择器selector,是不是有点类似观察者模式!!!!

先看服务端程序

首先定义通信数据包DataPacket


NIO单线程编写高性能、高并发服务器 - Seans Blog - Seans Blog


核心NIO服务

NIO单线程编写高性能、高并发服务器 - Seans Blog - Seans Blog

NIO单线程编写高性能、高并发服务器 - Seans Blog - Seans Blog

NIO单线程编写高性能、高并发服务器 - Seans Blog - Seans Blog


NIO单线程编写高性能、高并发服务器 - Seans Blog - Seans Blog



最后是测试程序

NIO单线程编写高性能、高并发服务器 - Seans Blog - Seans Blog


下面是客户端程序


NIO单线程编写高性能、高并发服务器 - Seans Blog - Seans Blog

NIO单线程编写高性能、高并发服务器 - Seans Blog - Seans Blog


运行结果

NIO单线程编写高性能、高并发服务器 - Seans Blog - Seans Blog

 

NIO单线程编写高性能、高并发服务器 - Seans Blog - Seans Blog

这就是Java  NIO机制,具有网络跨时代的意义,apache的顶级项目Mina就是采用Java NIO机制,目前Tomcat7也增加的NIO的实现,可见NIO的力量是非常强大的。

=============================================另外一个例子================================================

public class NBTest {


public NBTest() {
}


public void startServer() throws IOException{

int channels = 0;
int nKeys = 0;
int currentSelector = 0;


//使用Selector
Selector selector = Selector.open();

//建立Channel 并绑定到9000端口
ServerSocketChannel ssc = ServerSocketChannel.open();
InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(),9000); 
ssc.socket().bind(address);


//使设定non-blocking的方式。
ssc.configureBlocking(false);


//向Selector注册Channel及我们有兴趣的事件
SelectionKey s = ssc.register(selector, SelectionKey.OP_ACCEPT);
// printKeyInfo(s);
while(true){ //不断的轮询
System.out.println("NBTest: Starting select");

//Selector通过select方法通知我们我们感兴趣的事件发生了。
nKeys = selector.select();
//如果有我们注册的事情发生了,它的传回值就会大于0
if(nKeys > 0){
System.out.println("NBTest: Number of keys after select operation: " +nKeys);

//Selector传回一组SelectionKeys
//我们从这些key中的channel()方法中取得我们刚刚注册的channel。
Set selectedKeys = selector.selectedKeys();
Iterator i = selectedKeys.iterator();
while(i.hasNext()){
s = (SelectionKey) i.next();
printKeyInfo(s);
System.out.println("NBTest: Nr Keys in selector: " +selector.keys().size());

//一个key被处理完成后,就都被从就绪关键字(ready keys)列表中除去
i.remove();
if(s.isAcceptable()){
// 从channel()中取得我们刚刚注册的channel。
Socket socket = ((ServerSocketChannel)s.channel()).accept().socket();
SocketChannel sc = socket.getChannel();

sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ |SelectionKey.OP_WRITE);
System.out.println(++channels);
}else{
System.out.println("NBTest: Channel not acceptable");
}
}
}else{
System.out.println("NBTest: Select finished without any keys.");
}
}
}




private static void printKeyInfo(SelectionKey sk){
String s = new String();
s = "Att: " + (sk.attachment() == null ? "no" : "yes");
s += ", Read: " + sk.isReadable();
s += ", Acpt: " + sk.isAcceptable();
s += ", Cnct: " + sk.isConnectable();
s += ", Wrt: " + sk.isWritable();
s += ", Valid: " + sk.isValid();
s += ", Ops: " + sk.interestOps();
System.out.println(s);
}

public static void main(String[] args) throws IOException {
NBTest nbTest = new NBTest();
nbTest.startServer();
}
}

0 0
原创粉丝点击