nio的基础和Reactor的理解

来源:互联网 发布:linux怎么卸载jdk 编辑:程序博客网 时间:2024/04/29 15:27

nio的一些基础,还是感觉有些模糊,特别reactor

下面是我学习的一些代码,方便没事回顾一下:

public class MyTry {    public static void main(String[] args) {        try {            RandomAccessFile afile = new RandomAccessFile("aa.text", "rw");            FileChannel inchannel = afile.getChannel();            ByteBuffer buffer = ByteBuffer.allocate(48);            int read = inchannel.read(buffer);            while (read != -1) {               /* buffer.mark();                buffer.reset();*/                buffer.flip();                while (buffer.hasRemaining()) {                    System.out.println((char) buffer.get());                }                buffer.clear();                read = inchannel.read(buffer);            }            //scatter            /*ByteBuffer header = ByteBuffer.allocate(128);            ByteBuffer body   = ByteBuffer.allocate(1024);            ByteBuffer[] bufferArray = { header, body };            inchannel.read(bufferArray);*/            //gather            ByteBuffer header = ByteBuffer.allocate(128);            ByteBuffer body = ByteBuffer.allocate(1024);            //write data into buffers            ByteBuffer[] bufferArray = {header, body};            inchannel.write(bufferArray);            //transform            RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");            FileChannel fromChannel = fromFile.getChannel();            RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");            FileChannel toChannel = toFile.getChannel();            long position = 0;            long count = fromChannel.size();            toChannel.transferFrom(fromChannel, position, count);            //toChannel.transferFrom(position, count, fromChannel);            afile.close();            //selector            Selector selector = Selector.open();            Set selectedKeys = selector.selectedKeys();            Iterator keyIterator = selectedKeys.iterator();            while (keyIterator.hasNext()) {                SelectionKey key =(SelectionKey) keyIterator.next();                if (key.isAcceptable()) {                    // a connection was accepted by a ServerSocketChannel.                } else if (key.isConnectable()) {                    // a connection was established with a remote server.                } else if (key.isReadable()) {                    // a channel is ready for reading                } else if (key.isWritable()) {                    // a channel is ready for writing                }            }            /**             * block             * 通过 ServerSocketChannel.accept() 方法监听新进来的连接。             * 当 accept()方法返回的时候,它返回一个包含新进来的连接的 SocketChannel。             * 因此,accept()方法会一直阻塞到有新连接到达。             通常不会仅仅只监听一个连接,在while循环中调用 accept()方法. 如下面的例子:             */            //socket            /*ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();            serverSocketChannel.socket().bind(new InetSocketAddress(9999));*/            /*while(true){                SocketChannel socketChannel =                        serverSocketChannel.accept();                //do something with socketChannel...            }*/            /**             * ServerSocketChannel可以设置成非阻塞模式。在非阻塞模式下,             * accept() 方法会立刻返回,如果还没有新进来的连接,返回的将是null。             * 因此,需要检查返回的SocketChannel是否是null。             */            /*serverSocketChannel.configureBlocking(false);            while(true){                SocketChannel socketChannel =                        serverSocketChannel.accept();                if(socketChannel != null){                    //do something with socketChannel...                }            }*/            /**             * Datagram 通道             * Java NIO中的DatagramChannel是一个能收发UDP包的通道。因为UDP是无连接的网络协议,             * 所以不能像其它通道那样读取和写入。它发送和接收的是数据包。             *//*            DatagramChannel channel=DatagramChannel.open();            channel.socket().bind(new InetSocketAddress(9999));            ByteBuffer buf = ByteBuffer.allocate(48);            buf.clear();            channel.receive(buf);            //发送数据            //通过send()方法从DatagramChannel发送数据            String newData = "New String to write to file..." + System.currentTimeMillis();            ByteBuffer bf = ByteBuffer.allocate(48);            bf.clear();            bf.put(newData.getBytes());            //这个地方用flip的理解,首先我们写入之后,应该就是读取才会flip,为什么send的时候也要flip            //因为从buffer中要读入到channel中所以要flip,相对而言nenty的buf就不用            bf.flip();            int bytesSent = channel.send(buf, new InetSocketAddress("jenkov.com", 80));            //channel读写            /**             * 这个例子发送一串字符到”jenkov.com”服务器的UDP端口80。 因为服务端并没有监控这个端口,             * 所以什么也不会发生。也不会通知你发出的数据包是否已收到,因为UDP在数据传送方面没有任何保证。             * 连接到特定的地址             *             *             可以将DatagramChannel“连接”到网络中的特定地址的。由于UDP是无连接的,             连接到特定地址并不会像TCP通道那样创建一个真正的连接。而是锁住DatagramChannel ,             让其只能从特定地址收发数据。             */            //channel.connect(new InetSocketAddress("jenkov.com", 80));            //当连接后,也可以使用read()和write()方法,就像在用传统的通道一样。只是在数据传送方面没有任何保证。这里有几个例子            /*int bytesRead = channel.read(buf);            int bytesWritten = channel.write(but);*//**/            //向管道写数据            //要向管道写数据,需要访问sink通道。像这样:            Pipe pipe = Pipe.open();            Pipe.SinkChannel sinkChannel = pipe.sink();            String newData = "New String to write to file..." + System.currentTimeMillis();            ByteBuffer buf = ByteBuffer.allocate(48);            buf.clear();            buf.put(newData.getBytes());            buf.flip();            while(buf.hasRemaining()) {                sinkChannel.write(buf);            }            //从管道读取数据 从读取管道的数据,需要访问source通道,像这样            Pipe.SourceChannel sourceChannel = pipe.source();            ByteBuffer buff = ByteBuffer.allocate(48);            //read()方法返回的int值会告诉我们多少字节被读进了缓冲区。            int bytesRead = sourceChannel.read(buff);            /**             * 神总结             * 全文比较长,想打个比方归纳一下。             原文中说了最重要的3个概念,             Channel 通道             Buffer 缓冲区             Selector 选择器             其中Channel对应以前的流,Buffer不是什么新东西,Selector是因为nio可以使用异步的非堵塞模式才加入的东西。             以前的流总是堵塞的,一个线程只要对它进行操作,其它操作就会被堵塞,也就相当于水管没有阀门,你伸手接水的时候,             不管水到了没有,你就都只能耗在接水(流)上。             nio的Channel的加入,相当于增加了水龙头(有阀门),虽然一个时刻也只能接一个水管的水,但依赖轮换策略,在水量不大的时候,             各个水管里流出来的水,都可以得到妥善接纳,这个关键之处就是增加了一个接水工,也就是Selector,             他负责协调,也就是看哪根水管有水了的话,在当前水管的水接到一定程度的时候,就切换一下:临时关上当前水龙头,试             着打开另一个水龙头(看看有没有水)。             当其他人需要用水的时候,不是直接去接水,而是事前提了一个水桶给接水工,这个水桶就是Buffer。             也就是,其他人虽然也可能要等,但不会在现场等,而是回家等,可以做其它事去,水接满了,接水工会通知他们。             这其实也是非常接近当前社会分工细化的现实,也是统分利用现有资源达到并发效果的一种很经济的手段,             而不是动不动就来个并行处理,虽然那样是最简单的,但也是最浪费资源的方式             */        }catch (IOException e){            e.printStackTrace();        }        //基本的channel实例    }}

Reactor模式

就是将消息放到了一个队列中,通过异步线程池对其进行消费!

  • Reactor:Reactor是IO事件的派发者。
  • Acceptor:Acceptor接受client连接,建立对应client的Handler,并向Reactor注册此Handler。
  • Handler:和一个client通讯的实体,按这样的过程实现业务的处理。一般在基本的Handler基础上还会有更进一步的层次划分, 用来抽象诸如decode,process和encoder这些过程。比如对Web Server而言,decode通常是HTTP请求的解析, process的过程会进一步涉及到Listener和Servlet的调用。业务逻辑的处理在Reactor模式里被分散的IO事件所打破, 所以Handler需要有适当的机制在所需的信息还不全(读到一半)的时候保存上下文,并在下一次IO事件到来的时候(另一半可读了)能继续中断的处理。为了简化设计,Handler通常被设计成状态机,按GoF的state pattern来实现。

个人理解:原来的selector,nio服务端是c/s模式,所有的接受和注册都是selector管理,不过后面的io或者业务处理,只用一个线程。

reactor,是让每个channel接受过来后,让对应的handler处理,并且每一个io操作都是放入线程池中处理,注意其中的attench方法,将对应的handler与channel绑定

对于信息的上下文保存:结合了解netty的handler来理解觉得是这样的:

第一:handler中处理的都是工作线程,如果如要io操作的就放入线程池去处理,线程池处理是异步的,所以要有全局的上下文,来保存异步的结果,比如ctx的attribute或者channel的上下文,channel的作用范围要大一些,作用整个请求开始到结束,异步结果nenty中的是channelfuture,对于gof的实现不了解,之后再理解一下。

参考:

http://www.cnblogs.com/ivaneye/p/5731432.html

http://www.iteye.com/magazines/132-Java-NIO

学习代码:

/** * Created by yoyohunter on 17/1/16. */public class Reactor implements Runnable {    final Selector selector;    final ServerSocketChannel serverSocket;    Reactor(int port)throws IOException {        selector=Selector.open();        serverSocket= ServerSocketChannel.open();        InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(),port);        serverSocket.socket().bind(address);        serverSocket.configureBlocking(false);        //想selector注册 这个channel        SelectionKey sk=serverSocket.register(selector, SelectionKey.OP_ACCEPT);        System.out.println("start serversocket register");        //利用sk的attache功能绑定Acceptor 如果有事情,触发Acceptor        sk.attach(new Acceptor());        System.out.println("new acceptor");    }    public void run() {        try{            while (!Thread.interrupted()){                selector.select();                Set selected=selector.selectedKeys();                Iterator it=selected.iterator();                //Selector如果发现channel有OP_ACCEPT或READ事件发生,下列遍历就会进行。                while (it.hasNext())                    //来一个事件 第一次触发一个accepter线程                    //以后触发SocketReadHandler                    dispatch((SelectionKey)(it.next()));                    selected.clear();                }        }catch (IOException e){            e.printStackTrace();        }    }    //运行Acceptor或SocketReadHandler    void dispatch(SelectionKey k) {        Runnable r = (Runnable)(k.attachment());        if (r != null){            // r.run();        }    }    class Acceptor implements Runnable{        public void run() {            try{                SocketChannel  c=serverSocket.accept();                if(c !=null){                    //调用hanndler处理channel                    new SocketReadHandler(selector,c);                }            }catch (IOException e){                e.printStackTrace();            }        }    }}

                                             
0 0
原创粉丝点击