Reactor模式与NIO网络模型

在web开发中, 我们最常用的模式就是MVC,将web程序的不同部分分而治之。在Nio网络程序开发中,也有一些类似的开发模式,比如Reactor模式,来方便我们对程序进行解耦和控制。

维基百科对Reactor pattern的解释:

The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers

从定义可以看出,Reactor模式主要有以下特点:

  • 事件驱动
  • 请求分发
  • 事件处理

我们通过NIO的编程例子去理解这三个部分:

1、事件驱动:

在编写程序的过程中,最习惯的方式是:在哪种条件下,将执行哪段代码

if(condition1) {
    //event1
}
else if(condition2) {
    //event2
}
//...

现在,我们用多态的方式实现这个逻辑

pulic class Condition1 implements Runable {
    run() {
        //event1
    }
}

pulic class Condition2 implements Runable {
    run() {
        //event2
    }
}

Runable r = new Runable() //Condition1、Condition2 这两个其中一个
r.run()
...

通过这种方式,能够根据r实际的类型去执行对应的逻辑,对于Reator模式,也是如此,拿Nio网络编程来举例,我们会提前在每一个SelectionKey上注册对应的响应事件程序,等到事件发生时,再进行回调:

public class Reactor implements Runnable {

    final Selector selector;

    final ServerSocketChannel ssc;

    public Reactor(int port) throws IOException {
        selector = Selector.open();
        ssc  = ServerSocketChannel.open();
        ssc.socket().bind(new InetSocketAddress(port));
        ssc.configureBlocking(false);
        //注册事件处理程序Acceptor
        ssc.register(selector, SelectionKey.OP_ACCEPT, new Acceptor(selector, ssc));

    }

    public void run() {
        while (!Thread.interrupted()) {
            try {
                selector.select();
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> keyIterator = keys.iterator();
                while (keyIterator.hasNext()) {
                    dispatch(keyIterator.next());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    private void dispatch(SelectionKey key) {
        //事件发生时 回调事件响应程序
        Runnable r = (Runnable) key.attachment();
        if(r != null) {
            r.run();
        }
    }
}
public class Handler implements Runnable {

    final SelectionKey sk;

    final SocketChannel sc;

    static final int MAXIN = 10000, MAXOUT = 10000;

    ByteBuffer input = ByteBuffer.allocate(MAXIN);
    ByteBuffer output = ByteBuffer.allocate(MAXOUT);
    static final int READING = 0, SENDING = 1, PROCESSING = 3;
    int state = READING;

    static ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 10,
                                      0L,TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());

    public Handler(Selector selector, SocketChannel sc) throws IOException {
        this.sc = sc;
        sc.configureBlocking(false);
        //注册事件处理程序
        sk = sc.register(selector, SelectionKey.OP_READ, this);
        selector.wakeup();
    }

    public void run() {
        try {
            if (state == READING) {
                read();
            } else if (state == SENDING) {
                send();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //...
}

 

2、事件分发:

事件分发对应于多路复用的网路事件,即接收就绪、读就绪、写就绪和连接就绪。对于不同的事件,交由不同的逻辑处理。在例子中事件分发通过Java的多态隐式的进行了分发。

3、事件处理:

上面的过程干了两件事,响应请求和事件转发,第三件事情就是真正的处理请求,也就是事件处理Handler

public class Handler implements Runnable {

    final SelectionKey sk;

    final SocketChannel sc;

    static final int MAXIN = 10000, MAXOUT = 10000;

    ByteBuffer input = ByteBuffer.allocate(MAXIN);
    ByteBuffer output = ByteBuffer.allocate(MAXOUT);
    static final int READING = 0, SENDING = 1, PROCESSING = 3;
    int state = READING;

    static ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 10,
                                      0L,TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());

    public Handler(Selector selector, SocketChannel sc) throws IOException {
        this.sc = sc;
        sc.configureBlocking(false);
        sk = sc.register(selector, SelectionKey.OP_READ, this);
        selector.wakeup();
    }

    public void run() {
        try {
            if (state == READING) {
                read();
            } else if (state == SENDING) {
                send();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    synchronized void read() throws IOException {
        sc.read(input);
        if (inputIsComplete()) {
            state = PROCESSING;
            pool.execute(new Processor());
        }
    }

    synchronized void send() throws IOException {
        sc.write(output);
        if (outputIsComplete()) {
            sk.cancel();
        }
    }

    private boolean inputIsComplete() {
        return true;
    }

    private boolean outputIsComplete() {
        return true;
    }

    private void process() { /* ... */ }

    synchronized void processAndHandOff() {
        process();
        state = SENDING; // or rebind attachment
        sk.interestOps(SelectionKey.OP_WRITE);
    }

    public class Processor implements Runnable{

        @Override
        public void run() {
            processAndHandOff();
        }
    }
}

总结:对于Reactor的理解主要是将网络请求分为三个部分:响应请求,转发请求和处理请求。响应请求是基于注册响应程序的事件驱动的方式。在学习Reactor的过程中写了一个简单的demo:GitHub连接。本人对网络编程的认识也是刚刚开始,理解的不对的地方欢迎讨论,在此提前谢过。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值