Reactor设计模式

Reactor模式模块之间的交互

简单描述一下Reactor各个模块之间的交互流程,先从序列图开始:

1. 初始化InitiationDispatcher,并初始化一个Handle到EventHandler的Map。
2. 注册EventHandler到InitiationDispatcher中,每个EventHandler包含对相应Handle的引用,从而建立Handle到EventHandler的映射(Map)。
3. 调用InitiationDispatcher的handle_events()方法以启动Event Loop。在Event Loop中,调用select()方法(Synchronous Event Demultiplexer)阻塞等待Event发生。
4. 当某个或某些Handle的Event发生后,select()方法返回,InitiationDispatcher根据返回的Handle找到注册的EventHandler,并回调该EventHandler的handle_events()方法。
5. 在EventHandler的handle_events()方法中还可以向InitiationDispatcher中注册新的Eventhandler,比如对AcceptorEventHandler来,当有新的client连接时,它会产生新的EventHandler以处理新的连接,并注册到InitiationDispatcher中。

援引大神的文章:https://www.cnblogs.com/luxiaoxun/archive/2015/03/11/4331110.html

Basic Reactor Design

不引入线程池时服务端代码如下

/**
 *  创建 SocketHandler 的时候 会注册 对应的 读写事件,后续 如果有 事件发生, 会 由 reactor 调用 run方法
 *
 *  一个 socketHandler 对应 一个  selectionKey,也 对应 一组 ( sockelChannel 和 关注的 事件)
 *  当发生 selectionKey 对应的 事件 时,在 com.chunfen.tomcat.nio.reactor.Reactor#dispatch(java.nio.channels.SelectionKey)
 *  方法中 会调用run 方法,根据 channel 的 状态 进行 发送/接收数据
 */
public class SocketHandler implements Runnable{

    private SocketChannel socketChannel;
    private SelectionKey selectionKey;

    private ObjectMapper objectMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);

    ByteBuffer input = ByteBuffer.allocate(1024);
    ByteBuffer output = ByteBuffer.allocate(1024);

    public SocketHandler(Selector selector, SocketChannel socketChannel) throws IOException {
        this.socketChannel=socketChannel;
        socketChannel.configureBlocking(false);

        this.selectionKey = this.socketChannel.register(selector, 0);

        //将SelectionKey绑定为本Handler 下一步有事件触发时,将调用本类的run方法。
        //参看dispatch(SelectionKey key)
        selectionKey.attach(this);

        //同时将SelectionKey标记为可读,以便读取。
        selectionKey.interestOps(SelectionKey.OP_READ);
        // 可以 立即wakeup  也可不调用
//        selector.wakeup();
    }

    /**
     * 处理读取数据
     */
    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + " SocketHandler running");
            if(selectionKey.isReadable()){
                read();
            } else if(selectionKey.isWritable()){
                send();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    void read() throws IOException {
        input.clear();
        socketChannel.read(input);
        if (inputIsComplete()) {
            process();
            // Normally also do first write now
            selectionKey.interestOps(SelectionKey.OP_WRITE); //第三步,接收write事件
            // 可以 立即wakeup  也可不调用
//            selectionKey.selector().wakeup();
        }
    }
    void send() throws IOException {

        output.clear();
        Message<Date> dateMessage = new Message<Date>();
        dateMessage.setData(new Date());
        dateMessage.setCode(11);

        System.out.println(Thread.currentThread().getName() + " SocketHandler send message" + objectMapper.writeValueAsString(dateMessage));

        socketChannel.write(ByteBuffer.wrap(objectMapper.writeValueAsBytes(dateMessage)));
        //write完就结束了, 关闭select key
        if (outputIsComplete()) {
            selectionKey.cancel();
        }
    }

    private boolean inputIsComplete() {
        /* ... */
        return true;
    }

    private boolean outputIsComplete() {

        /* ... */
        return true;
    }

    private void process() throws IOException {
        /* ... */
        input.flip();
        byte[] bytes = new byte[input.remaining()];
        input.get(bytes);
//        Message obj = objectMapper.readValue(bytes, Message.class);
        System.out.println(Thread.currentThread().getName() + " SocketHandler read message"+new String(bytes, "utf-8"));
        return;
    }
}
/**
 * 初始 reactor 关注 accept 事件,dispatch 会 调用 accept 的run 方法
 * 拿到 socketChannel 的时候 创建  socketHandler 让其 执行 连接完成后的  后续方法
 */
public class Acceptor implements Runnable {

    private Reactor reactor;

    public Acceptor(Reactor reactor){
        this.reactor=reactor;
    }

    @Override
    public void run() {
        try {
            SocketChannel socketChannel=reactor.getServerSocketChannel().accept();
            System.out.println(Thread.currentThread().getName() + " Acceptor accept a client");
            //调用Handler来处理channel
            if(socketChannel!=null){
                new SocketHandler(reactor.getSelector(), socketChannel);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

@Getter
@Setter
public class Reactor implements Runnable{

    private ObjectMapper objectMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);

    private Selector selector;
    private ServerSocketChannel serverSocketChannel;

    public Reactor(int port){
        try {
            selector=Selector.open();
            serverSocketChannel=ServerSocketChannel.open();
            InetSocketAddress inetSocketAddress=new InetSocketAddress(InetAddress.getLocalHost(),port);
            serverSocketChannel.bind(inetSocketAddress);
            serverSocketChannel.configureBlocking(false);

            //向selector注册该channel
            SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            //利用selectionKey的attache功能绑定Acceptor 如果有事情,触发Acceptor
            selectionKey.attach(new Acceptor(this));
            System.out.println(Thread.currentThread().getName() + " Reactor starting");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + " Reactor started");
            while(!Thread.interrupted()){
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                //Selector如果发现channel有OP_ACCEPT或READ事件发生,下列遍历就会进行。
                while(iterator.hasNext()){

                    //来一个事件 第一次触发一个accepter线程
                    //以后触发SocketReadHandler
                    SelectionKey selectionKey = iterator.next();
                    System.out.println(Thread.currentThread().getName() + " Reactor from client select" + objectMapper.writeValueAsString(selectionKey));

                    //所有的关心的 io 事件 都 通过 dispatch 进行分发
                    dispatch(selectionKey);
    //                selectionKeys.clear();
                    iterator.remove();
                }
            }

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

    /**
     * 运行Acceptor或SocketReadHandler
     * @param key
     */
    void dispatch(SelectionKey key) {
        // 不同的 key 触发不同的 事件
        // 注意 此处 attachment 不一定 必须是 Runnable 接口的实现,也可以是公共的接口
        Runnable r = (Runnable)(key.attachment());
        if (r != null){
            r.run();
        }
    }
}

客户端代码如下

    @Test
    public void reactorTest() throws Exception{

        new Thread(new Reactor(8889)).start();
        System.out.println(Thread.currentThread().getName() + " ReactorTest reactor starting");
        Thread.sleep(300);

        try (SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(InetAddress.getLocalHost(), 8889))){
            System.out.println(Thread.currentThread().getName() + " ReactorTest client connected");
            socketChannel.write(Charset.forName("utf-8").encode("Hi~ server have a nice 假日"));

            ByteBuffer input = ByteBuffer.allocate(1024);
            socketChannel.read(input);

            input.flip();
            byte[] bytes = new byte[input.remaining()];
            input.get(bytes);

            Message obj = objectMapper.readValue(bytes, Message.class);
            System.out.println(Thread.currentThread().getName() + " " + obj.toString());

            // 维持  main
            Thread.sleep(20 * 1000);

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

日志输出如下

main Reactor starting
main ReactorTest reactor starting
Thread-0 Reactor started
main ReactorTest client connected
Thread-0 Reactor from client select{"valid":true,"selector":{"open":true},"readable":false,"writable":false,"connectable":false,"acceptable":true}
Thread-0 Acceptor accept a client
Thread-0 Reactor from client select{"valid":true,"selector":{"open":true},"readable":true,"writable":false,"connectable":false,"acceptable":false}
Thread-0 SocketHandler running
Thread-0 SocketHandler read messageHi~ server have a nice 假日
Thread-0 Reactor from client select{"valid":true,"selector":{"open":true},"readable":false,"writable":true,"connectable":false,"acceptable":false}
Thread-0 SocketHandler running
Thread-0 SocketHandler send message{"code":11,"data":1569831928922}
main Message(code=11, data=1569831928922, message=null)

socketHanlder的两个判断可以改为状态模式(State-Object Pattern)

 /**
     *
     * 处理读取数据
     *
     */
    @Override
    public void run() {
//        方式一: 回调对象只有  socketHandler 所以 要进行 selectKey 可读可写状态判断
//        try {
//            System.out.println(Thread.currentThread().getName() + " SocketHandler running");
//            if(selectionKey.isReadable()){
//                read();
//            } else if(selectionKey.isWritable()){
//                send();
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        }

//        方式二  使用状态模式(State-Object Pattern)
//        selectionKey 是 context 环境  attachment 就是  state
//        更换 attachment  reactor 的 dispatch 方法 会执行不同的回调
        try {
            socketChannel.read(input);
            if (inputIsComplete()) {
                process();
                selectionKey.attach(new Sender());  //状态迁移, Read后变成write, 用Sender作为新的callback对象
                selectionKey.interestOps(SelectionKey.OP_WRITE);
                selectionKey.selector().wakeup();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    class Sender implements Runnable {
        public void run(){
            try {
                send();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

也可单独分离readHandler和sendHandler,详见git代码

Worker Thread Pools

对acceptor引入线程池

reactor中更换acceptor如下

public Reactor(int port){
        try {
            selector=Selector.open();
            serverSocketChannel=ServerSocketChannel.open();
            InetSocketAddress inetSocketAddress=new InetSocketAddress(InetAddress.getLocalHost(),port);
            serverSocketChannel.bind(inetSocketAddress);
            serverSocketChannel.configureBlocking(false);

            //向selector注册该channel
            SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            //利用selectionKey的attache功能绑定Acceptor 如果有事情,触发Acceptor
//            selectionKey.attach(new Acceptor(this));
            //使用线程池 的 acceptor
            selectionKey.attach(new PoolAcceptor(this));
            System.out.println(Thread.currentThread().getName() + " Reactor starting");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

acceptor改写如下:

/**
 * acceptor 使用线程池 处理 读时间
 */
public class PoolAcceptor implements Runnable {

    private Reactor reactor;

    private ThreadPoolExecutor pool;

    static final int PROCESSING = 3;

    public PoolAcceptor(Reactor reactor){
        this.reactor=reactor;
        pool = new ThreadPoolExecutor(3, 5, 1000, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100), new ThreadPoolExecutor.AbortPolicy());
    }

    @Override
    public void run() {
        try {
            SocketChannel socketChannel=reactor.getServerSocketChannel().accept();
            System.out.println(Thread.currentThread().getName() + " Acceptor accept a client");
            //调用Handler来处理channel
            if(socketChannel!=null){
                if(PROCESSING == 3) {
                    pool.execute(new ReadHandler(reactor.getSelector(), socketChannel));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Using Multiple Reactors

主Reactor只进行accept,从Reactor进行读写操作

增加SubReactor如下:

public class SubReactor extends Thread{

    private ObjectMapper objectMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);

    private Selector selector;

    ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 1000,TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100), new ThreadPoolExecutor.AbortPolicy());

    public SubReactor(){
        try {
            selector = Selector.open();
            System.out.println(Thread.currentThread().getName() + " SubReactor selector open");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void register(SocketChannel socketChannel){
        try {
            System.out.println(Thread.currentThread().getName() + " SubReactor register socketChannel");
            // 特别提示: 一个坑 如果 selector 优先 被select() 方法阻塞, 要优先调用 wakeup()方法,再进行 register 的动作,如果是 线程池里面进行register,那么在注册前调用wakeup()方法
//            selector.wakeup();
//            new ReadHandler(selector, socketChannel);
            pool.execute(new ReadHandler(selector, socketChannel));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        //同原来 reactor 方法
//selector 从这里阻塞
    }

    /**
     * 运行Acceptor或SocketReadHandler
     * @param key
     */
    void dispatch(SelectionKey key) {
         //同原来 reactor 方法       
    }
}

增加MainReactor如下:

public class MainReactor implements Runnable{

    private ObjectMapper objectMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);

    private Selector selector;
    private ServerSocketChannel serverSocketChannel;

    public MainReactor(int port){
        try {
            selector=Selector.open();
            serverSocketChannel=ServerSocketChannel.open();
            InetSocketAddress inetSocketAddress=new InetSocketAddress(InetAddress.getLocalHost(),port);
            serverSocketChannel.bind(inetSocketAddress);
            serverSocketChannel.configureBlocking(false);

            //向selector注册该channel
            SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            //注意这里,从这里创建 subReactor
            SubReactor subReactor = new SubReactor();
            subReactor.start();
            
            selectionKey.attach(new MainSubAcceptor(this, subReactor));
            System.out.println(Thread.currentThread().getName() + " Reactor starting");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
       //同原来的reactor方法
    }

    /**
     * 运行Acceptor或SocketReadHandler
     * @param key
     */
    void dispatch(SelectionKey key) {
//同原来的reactor方法
    }
}

增加MainSubAcceptor如下

/**
 * 初始 reactor 关注 accept 事件,dispatch 会 调用 accept 的run 方法
 * 拿到 socketChannel 的时候 创建  socketHandler 让其 执行 连接完成后的  后续方法
 */
public class MainSubAcceptor implements Runnable {

    private MainReactor reactor;

    private SubReactor subReactor;

    public MainSubAcceptor(MainReactor reactor, SubReactor subReactor){
        this.reactor=reactor;
        this.subReactor = subReactor;
    }

    @Override
    public void run() {
        try {
            SocketChannel socketChannel= reactor.getServerSocketChannel().accept();
            System.out.println(Thread.currentThread().getName() + " MainSubAcceptor accept a client");
            //调用Handler来处理channel
            if(socketChannel!=null){
                // 使用 分离的 reactor 进行 后续 处理
                subReactor.register(socketChannel);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " MainSubAcceptor run end");
    }
}

遇到的坑:再做左后一个demo时遇到一个坑,如果SubReactor优先启动,selector 会优先调用 select() 阻塞,这时如果要再进行 register 那么在调用前必须先 进行 selector.wakeup() 才能继续进行数据发送和接收。

完整的代码演示请移步gitee https://gitee.com/xic/chunfenwx/tree/master/tomcat-demo

参考资料:

https://www.cnblogs.com/yueweimian/p/6262211.html

https://www.cnblogs.com/luxiaoxun/archive/2015/03/11/4331110.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值