zoukankan      html  css  js  c++  java
  • Java NIO 与 基于reactor设计模式的事件处理模型

    Java NIO非堵塞应用通常适用用在I/O读写等方面,我们知道,系统运行的性能瓶颈通常在I/O读写,包括对端口文件的操作上,过去,在打开一个I/O通道后,read()将一直等待在端口一边读取字节内容,如果没有内容进来,read()也是傻傻的等,这会影响我们程序继续做其他事情,那么改进做法就是开设线程,让线程去等待,但是这样做也是相当耗费资源的。

    Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。

    Java NIO出现不只是一个技术性能的提高,你会发现网络上到处在介绍它,因为它具有里程碑意义,从JDK1.4开始,Java开始提高性能相关的功能,从而使得Java在底层或者并行分布式计算等操作上已经可以和C或Perl等语言并驾齐驱。

    如果你至今还是在怀疑Java的性能,说明你的思想和观念已经完全落伍了,Java一两年就应该用新的名词来定义。从JDK1.5开始又要提供关于线程、并发等新性能的支持,Java应用在游戏等适时领域方面的机会已经成熟,Java在稳定自己中间件地位后,开始蚕食传统C的领域。

    NIO的基本原理:

      NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的socketchannel告诉Selector,我们接着做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的socketchannel,然后,我们从这个Channel中读取数据,放心,包准能够读到,接着我们可以处理这些数据。

      Selector内部原理实际是在做一个对所注册的channel的轮询访问,不断的轮询(目前就这一个算法),一旦轮询到一个channel有所注册的事情发生,比如数据来了,他就会站起来报告,交出一把钥匙,让我们通过这把钥匙来读取这个channel的内容。

    使用上:

    我们结合代码看看使用,在使用上,也在分两个方向,一个是线程处理,一个是用非线程,后者比较简单,看下面代码:

    package reactor.section3;
    
    import java.net.InetAddress;
    import java.net.InetSocketAddress;
    import java.net.Socket;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    import java.util.Set;
    
    public class NBTest1 {
        
        
    
        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();
            debug(s);
        }
        private static void debug(String s) {
            System.out.println(s);
        }
        public static void main(String args[]) {
            NBTest1 nbTest = new NBTest1();
            try {
                nbTest.startServer();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        
        private void startServer()  throws Exception{
            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) //不断的轮询
            {
                
                debug("NBTest: Starting select");
                //Selector通过select方法通知我们我们感兴趣的事件发生了。
                nKeys = selector.select();
                //如果有我们注册的事情发生了,它的传回值就会大于0
                if(nKeys > 0){
                    debug("NBTest: Number of keys after select operation: " +nKeys);
                    
                    Set selectedKeys = selector.selectedKeys();
                    Iterator i = selectedKeys.iterator();
                    while(i.hasNext()){
                        
                        s = (SelectionKey) i.next();
                        printKeyInfo(s);
                        debug("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{
                            debug("NBTest: Channel not acceptable");
                        }
                    }
                    
                    
                    
                }else{
                    debug("NBTest: Select finished without any keys.");
                }
    
            }
            
        }
    }

    这是一个守候在端口9000的noblock server例子,如果我们编制一个客户端程序,就可以对它进行互动操作,或者使用telnet 主机名 90000 可以链接上。

    当前分布式计算 Web Services盛行天下,这些网络服务的底层都离不开对socket的操作。他们都有一个共同的结构:
    1. Read request
    2. Decode request
    3. Process service
    4. Encode reply
    5. Send reply

    写过大中型网络服务器的朋友相信对事件处理模型(有时也叫事件触发模型)不陌生。今天要讲的Reactor就是在事件处理模型中用的比较多的一种设计模式。请大家先看下面的图,有个初步的印象:

    reactor模式类结构图

    在上图中,可以看到主要有以下四种角色:

    1. Reactor:
    Reactor是Reactor模式中最为关键的角色,它是该模式最终向用户提供接口的类。用户可以向Reactor中注册EventHandler(3),然后Reactor在“反应(react)”的时候,发现用户注册的fd上有事件发生,

    就会回调用户的事件处理函数。

    2. SynchrousEventDemultiplexer:

    SynchrousEventDemultiplexer也是Reactor中一个比较重要的角色,它是Reactor用来检测用户注册的fd上发生的事件的利器,通过过Reactor得知了哪些fd上发什么了什么样的事件,然后以些为依据,来多路分发事件,回调用户的事件处理函数。

    3. EventHandler:
    EventHander是用户和Reactor打交道的工具,用户通过向Reactor注册自己的EventHandler,可以告知Reactor在特定事件发生的时候该帮我做些什么。

    4. ConcreteEventHandler:
    ConcreteEventHandler是EventHandler的子类,EventHandler是Reactor所用来规定接口的基类,用户自己的事件处理器都必须从EventHandler继承。

    【java reactor代码】

    Java的NIO为reactor模式提供了实现的基础机制,它的Selector当发现某个channel有数据时,会通过SlectorKey来告知我们,在此我们实现事件和handler的绑定。

    参考:java nio  http://www.jdon.com/concurrent/nio.pdf

    http://www.cnblogs.com/xuekyo/archive/2013/01/20/2868547.html

  • 相关阅读:
    Time Zone 【模拟时区转换】(HDU暑假2018多校第一场)
    HDU 1281 棋盘游戏 【二分图最大匹配】
    Codeforces Round #527 (Div. 3) F. Tree with Maximum Cost 【DFS换根 || 树形dp】
    Codeforces Round #527 (Div. 3) D2. Great Vova Wall (Version 2) 【思维】
    Codeforces Round #527 (Div. 3) D1. Great Vova Wall (Version 1) 【思维】
    Codeforces Round #528 (Div. 2, based on Technocup 2019 Elimination Round 4) C. Connect Three 【模拟】
    Avito Cool Challenge 2018 E. Missing Numbers 【枚举】
    Avito Cool Challenge 2018 C. Colorful Bricks 【排列组合】
    005 如何分析问题框架
    004 如何定义和澄清问题
  • 原文地址:https://www.cnblogs.com/joqk/p/3972402.html
Copyright © 2011-2022 走看看