JavaNIO多Reactor模式实现echo服务器

多Reactor模型

è¿éåå¾çæè¿°

        多Reactor模型是netty使用的模型,从图中可以直接看出,我们实现了一个主反应器(mainReactor)和多个次反应器(subReactor),主反应器中包含了一个ServerSocketChannel用来接收所有的请求,主反应器中的选择器通过轮询方式将新链接交由不同的次反应器,次反应器维护主反应器交付的SocketChannel,次反应器负责链接的读写监控和处理。

       本来想着从网上copy一段代码,结果没有搜到我想要的实现了Reactor模型--多Reactor模型代码,本人花了一点功夫实现了,供大家参考。注意:代码可能产生jdk Epoll空轮询导致cpu 100%的著名jdk nio bug问题,解决方法:可以给select加一个超时时间,当select未在规定时间阻塞并且不是因为注册唤醒的次数达到阈值(例如:512次)可以认为发生了空轮询,此时需要重新open一个selector将旧selector上的channal全部转移注册到新selector上。
 

package com.nio.server3;

import java.io.EOFException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Iterator;
import java.util.concurrent.*;

/**
 *
 */
public class NioServer3 {

    private int port;
    private Selector selector;
    private SelectorProvider provider;
    private int numCores = 3;
    private int index = 0;

    private ExecutorService service = Executors.newFixedThreadPool(numCores);
    private SubReactor[] subReactors = new SubReactor[numCores];

    public static void main(String[] args){
        //启动mainReactor
        new NioServer3(7001).start();
    }

    public NioServer3(int port) {
        this.port = port;
    }

    public void init() {
        ServerSocketChannel ssc = null;
        try {
            provider = SelectorProvider.provider();
            ssc = ServerSocketChannel.open();
            ssc.configureBlocking(false);
            ssc.bind(new InetSocketAddress(port));
            selector = provider.openSelector();
            ssc.register(selector, SelectionKey.OP_ACCEPT);

            for(int i = 0;i< subReactors.length;i++){
                subReactors[i] = new SubReactor();
            }
            //启动subReactor线程
            for(SubReactor subReactor : subReactors){
                service.submit(subReactor);
            }
            System.out.println("NioServer started ......");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
        }
    }

    public void accept(SelectionKey key) {

        try {
            SubReactor handler = this.subReactors[index % numCores];
            index ++;
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            SocketChannel sc = ssc.accept();
            sc.configureBlocking(false);
            //直接注册会阻塞,采用队列解决阻塞问题
            handler.register(sc);
            sc.write(ByteBuffer.wrap("Java NIO Reactor Copyright belongs to CSDN \r\nReactor> ".getBytes()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start() {

        this.init();

        while (true) {
            try {
                int events = selector.select();

                if (events > 0) {
                    Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();

                    while (selectionKeys.hasNext()) {
                        SelectionKey key = selectionKeys.next();
                        selectionKeys.remove();

                        if (key.isAcceptable()) {
                            accept(key);
                        }
                    }
                }
            } catch (IOException  e) {
                e.printStackTrace();
            }
        }
    }

    public static class SubReactor implements Runnable{

        private Selector selector;
        private ConcurrentLinkedQueue<SocketChannel> event = new ConcurrentLinkedQueue<>();
        private ByteBuffer inBuffer = ByteBuffer.allocateDirect(1024);  // 创建读取缓冲区
        private ByteBuffer outBuffer = ByteBuffer.allocateDirect(1024);  // 创建写缓冲区
        private StringBuilder request = new StringBuilder();
        private String READING = "READING",CLOSED = "CLOSED";
        private String STATE = READING;

        public SubReactor() throws IOException {
            this.selector = Selector.open();
        }

        public void register(SocketChannel sc){
            event.offer(sc);
            selector.wakeup();
        }


        protected boolean inputIsComplete() throws IOException {
            while (inBuffer.hasRemaining()) {

                byte ch = inBuffer.get();

                if (ch == 3) { // ctrl+c 关闭连接
                    STATE = CLOSED;
                    return true;
                } else if (ch == '\r') { // continue
                } else if (ch == '\n') {
                    // 读取到了 \r\n 读取结束
                    return true;
                } else {
                    request.append((char)ch);
                }
            }
            return false;
        }



        public void readAndReply(SelectionKey key) {


            try{
                SocketChannel channel = (SocketChannel) key.channel(); // 服务器可读取消息:得到事件发生的Socket通道(其实就是这个请求对应的通道)
                inBuffer.clear();
                int read = channel.read(inBuffer);
                if (read > 0) {
                    inBuffer.flip();
                    if(inputIsComplete()){
                        if(STATE == CLOSED){
                            key.channel().close();
                            STATE = READING;
                            return;
                        }
                        System.out.println("收到消息:" + request.toString()+"\r\n");
                        //让线程睡眠一会模拟业务处理
//                    Thread.sleep(5000);
                        outBuffer.clear();
                        outBuffer.put((request.toString()+"\r\nReactor> ").getBytes());
                        outBuffer.flip();
                        channel.write(outBuffer); // 将消息回送给客户端
                        request.delete(0,request.length());
                    }
                } else if (read == -1){
                    // -1 客户端关闭了连接
                    throw new EOFException();
                }else{
                   //read = 0  不处理
                }

            } catch (IOException  e) {
                try {
                    key.channel().close();
                } catch (IOException ex) {
                }
            }

        }

        @Override
        public void run() {
            try {

                while (true) {
                    SocketChannel sc = null;
                    while( (sc = event.poll()) != null){

                        sc.register(selector,SelectionKey.OP_READ);
                        System.out.println(sc.socket().getRemoteSocketAddress() +" 注册到了线程:"+Thread.currentThread().getName());
                    }

                    selector.select();
                    Iterator<?> ite = this.selector.selectedKeys().iterator();
                    while (ite.hasNext()) {
                        SelectionKey key = (SelectionKey) ite.next();
                        ite.remove();  // 删除已选的key,以防重复处理
                        if(key.isReadable()){
                          readAndReply(key);
                        }
                    }
                }

            } catch (IOException  e) {
                e.printStackTrace();
            }
        }
    }


}

猜你喜欢

转载自blog.csdn.net/qq_32445015/article/details/104584433
今日推荐