Netty入门(五) Netty原理之Reactor模式

Netty完整源码+代码示例地址:http://docs.52im.net/extend/docs/src/netty4/

Netty API地址:https://netty.io/4.1/api/index.html

一.Reactor模式

1.概念:Reactor模式又称为反应器模式,是一种处理同步IO通信的设计模式,应用于同步IO场景,基于事件驱动。传统的IO操作会为每一个连接或请求建立一个单独的线程然后执行IO操作阻塞在那里,这样非常的浪费资源和效率。 Reactor设计模式能够避免为每个消息,请求,连接创建一个线程的问题,它采用事件驱动,将业务处理器感兴趣的事件注册到分发器上,只有当相应事件发生时,分发器才会通知相应的处理器处理发生的事务--“We wlill call you”

2.模式组成部分

Netty入门(五) Netty原理之Reactor模式_第1张图片

(1)Handle:即操作系统中的句柄,是对资源在操作系统层面上的一种抽象,它可以是打开的文件、一个连接(Socket)、Timer等。由于Reactor模式一般使用在网络编程中,因而这里一般指Socket 连接,即一个网络连接(Connection,在Java NIO中的Channel)。这个Channel注册到Synchronous Event Demultiplexer中,以监听Handle中发生的事件,对ServerSocketChannnel可以是CONNECT事件,对SocketChannel可以是READ、WRITE、CLOSE事件等。
(2)Synchronous Event Demultiplexer:阻塞等待一系列的Handle中的事件到来,如果阻塞等待返回,即表示在返回的Handle中可以不阻塞的执行返回的事件类型。这个模块一般使用操作系统的select来实现。在Java NIO中用Selector来封装,当Selector.select()返回时,可以调用Selector的selectedKeys()方法获取发生的事件
(3)Initiation Dispatcher:用于管理Event Handler,即EventHandler的容器,用以注册、移除EventHandler等;另外,它还作为Reactor模式的入口调用Synchronous Event Demultiplexer的select方法以阻塞等待事件返回,当阻塞等待返回时,根据事件发生的Handle将其分发给对应的Event Handler处理,即回调EventHandler中的handle_event()方法。
(4)Event Handler:定义事件处理方法:handle_event(),以供InitiationDispatcher回调使用。
(5)Concrete Event Handler:事件EventHandler接口,实现特定事件处理逻辑。比如有专门处理读的ReadHandler,专门处理写的WriteHandler

 

 

二.同步异步阻塞非阻塞

(1)同步和异步:同步A调用B,B处理直到获得结果,才返回给A。需要调用者一直等待和确认调用结果是否返回,然后继续往下执行。异步A调用B,无需等待结果,B通过状态通知A或回调函数来处理。调用结果返回时,会以消息或回调的方式通知调用者。同步=主动询问,异步=他发生时会主动告诉你
(2)阻塞非阻塞:阻塞A调用B,A被挂起直到B返回结果给A,才能继续执行。调用结果返回前,当前线程挂起不能够处理其他任务,一直等待调用结果返回。非阻塞A调用B,A不会被挂起,A可以执行其他操作。调用结果返回前,当前线程不挂起,可以处理其他任务。
(3)同步异步是个操作方式(获得响应的一种方式),阻塞非阻塞是线程的一种状态。同步异步指的是被调用者结果返回时通知线程的一种机制,阻塞非阻塞指的是调用结果返回进程前的状态,是挂起还是继续处理其他任务。

(4)例子(来源于网络,侵删):

故事:老王烧开水。

出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。

老王想了想,有好几种等待方式

1.老王用水壶煮水,并且站在那里,不管水开没开,每隔一定时间看看水开了没。-同步阻塞

老王想了想,这种方法不够聪明。

2.老王还是用水壶煮水,不再傻傻的站在那里看水开,跑去寝室上网,但是还是会每隔一段时间过来看看水开了没有,水没有开就走人。-同步非阻塞

老王想了想,现在的方法聪明了些,但是还是不够好。

3.老王这次使用高大上的响水壶来煮水,站在那里,但是不会再每隔一段时间去看水开,而是等水开了,水壶会自动的通知他。-异步阻塞

老王想了想,不会呀,既然水壶可以通知我,那我为什么还要傻傻的站在那里等呢,嗯,得换个方法。

4.老王还是使用响水壶煮水,跑到客厅上网去,等着响水壶自己把水煮熟了以后通知他。-异步非阻塞

老王豁然,这下感觉轻松了很多。

三.Java NIO与Reactor模式

1.Java NIO对Reactor的支持

Java NIO是一种同步非阻塞的IO,同步是因为NIO需要主动去轮询事件的发生,非阻塞是因为IO操作是非阻塞的。

NIO对Reactor模式有无缝的支持,即使用Selector类封装了操作系统提供的Synchronous Event Demultiplexer功能。NIO中Reactor的核心是Selector,Selector内部原理实际是在做一个对所注册的channel的轮询访问,不断的轮询(目前就这一个算法),一旦轮询到一个channel有所注册的事情发生,比如数据来了,他就会通知我们我们感兴趣的事件发生了,交出一把钥匙,让我们通过这把钥匙来读取这个channel的内容,而不需要我们主动去轮询事件了。一个传统的NIO代码其实就是一个简单的Reactor模式,通过select()获取发生事件,然后进行相应的事件处理。

2.Java NIO Reactor模式的封装实现

Synchronous Event Demultiplexer:由Selector封装,提供select()方法获取事件

Initiation Dispatcher:封装为一个Reactor类,其内部有一个Selector进行轮询,Reactor根据事件分发Handler

Event Handler:使用SelectionKey中的Attachment来存储事件对应的EventHandler对象,因而不需要注册EventHandler这个步骤,或者设置Attachment就是这里的注册。

(1)单线程Reactor模式

Netty入门(五) Netty原理之Reactor模式_第2张图片

最简单的单Reactor单线程模型。Reactor线程是个多面手,负责多路分离套接字,Accept新连接,并分派请求到Handler处理器中。Acceptor是个专门处理连接事件的Handler,缺点是当其中某个 handler 阻塞时, 会导致其他所有的 client 的 handler 都得不到执行, 并且更严重的是, handler 的阻塞也会导致整个服务不能接收新的 client 请求(因为 acceptor 也被阻塞了)。代码如下:

class Reactor implements Runnable { //封装Reactor负责处理连接,分配handler
    final Selector selector;//封装Synchronous Event Demultiplexer,实现轮询事件通知
    final ServerSocketChannel serverSocket;
    Reactor(int port) throws IOException { //Reactor初始化
        selector = Selector.open();
        serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(new InetSocketAddress(port));
        serverSocket.configureBlocking(false); //非阻塞
        SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT); //分步处理,第一步,注册accept事件
        sk.attach(new Acceptor()); //通过attach方法将处理accept的handler对象附加给相应的selectionkey,相当于注册handler
    }
    
    public void run() { 
        try {
            while (!Thread.interrupted()) {
                selector.select();
                Set selected = selector.selectedKeys();
                Iterator it = selected.iterator();
                while (it.hasNext())//事件通知
                    dispatch((SelectionKey)(it.next()); //Reactor负责dispatch收到的事件
                selected.clear();
            }
        } catch (IOException ex) { /* ... */ }
    }
    
    void dispatch(SelectionKey k) {
        Runnable r = (Runnable)(k.attachment()); //调用之前注册的callback对象
        if (r != null)
            r.run();//处理事务
    }
    
    class Acceptor implements Runnable { //内部类,连接处理handler
        public void run() {
            try {
                SocketChannel c = serverSocket.accept();
                if (c != null)
                new Handler(selector, c);//分配新的handler
            }
            catch(IOException ex) { /* ... */ }
        }
    }
}
//一个handler根据判断同时处理read和write

final class Handler implements Runnable {
    final SocketChannel socket;
    final SelectionKey sk;
    ByteBuffer input = ByteBuffer.allocate(MAXIN);
    ByteBuffer output = ByteBuffer.allocate(MAXOUT);
    static final int READING = 0, SENDING = 1;
    int state = READING;
    
    Handler(Selector sel, SocketChannel c) throws IOException {
        socket = c; c.configureBlocking(false);
        // Optionally try first read now
        sk = socket.register(sel, 0);
        sk.attach(this); //将Handler作为callback(回调)对象
        sk.interestOps(SelectionKey.OP_READ); //第二步,注册Read事件
        sel.wakeup();//唤醒select()
    }
    boolean inputIsComplete() { /* ... */ }
    boolean outputIsComplete() { /* ... */ }
    void process() { /* ... */ }
    
    public void run() {//处理事务
        try {
            if (state == READING) read();
            else if (state == SENDING) send();
        } catch (IOException ex) { /* ... */ }
    }
    
    void read() throws IOException {
        socket.read(input);
        if (inputIsComplete()) {
            process();
            state = SENDING;
            // Normally also do first write now
            sk.interestOps(SelectionKey.OP_WRITE); //第三步,接收write事件
        }
    }
    void send() throws IOException {
        socket.write(output);
        if (outputIsComplete()) sk.cancel(); //write完就结束了, 关闭select key
    }
}


//可以用State-Object pattern来更优雅的实现,read和write处理分离
class Handler { // ...
    public void run() { // initial state is reader
        socket.read(input);
        if (inputIsComplete()) {
            process();
            sk.attach(new Sender());  //状态迁移, Read后变成write, 用Sender作为新的callback对象
            sk.interest(SelectionKey.OP_WRITE);
            sk.selector().wakeup();
        }
    }
    class Sender implements Runnable {
        public void run(){ // ...
            socket.write(output);
            if (outputIsComplete()) sk.cancel();
        }
    }
}

(2)单Reactor多线程模式

Netty入门(五) Netty原理之Reactor模式_第3张图片

在线程Reactor模式基础上,做如下改进:将Handler处理器的执行放入线程池,多线程进行业务处理。防止handler阻塞,将请求接收和事务处理分离开来。代码如下:

class Handler implements Runnable {
    // uses util.concurrent thread pool
    static PooledExecutor pool = new PooledExecutor(...);//线程池分配线程
    static final int PROCESSING = 3;
    // ...
    synchronized void read() { // ...
        socket.read(input);
        if (inputIsComplete()) {
            state = PROCESSING;
            pool.execute(new Processer()); //使用线程pool异步执行
        }
    }
    
    synchronized void processAndHandOff() {
        process();
        state = SENDING; // or rebind attachment
        sk.interest(SelectionKey.OP_WRITE); //process完,开始等待write事件
    }
    

    /*
     *内部类
     *内部类可以使用外部类的任何属性和方法,可使用Out.this声明
     *内部类的创建必须拥有外部类的实例: new Out().new Inner()
     *内部类持有外部类的引用,因此可以将两个线程联系起来
     */
    /*
     *线程
     *没有父子线程之说,线程都是相互独立的
     *线程之间互不影响包括生命周期
     */
    class Processer implements Runnable {
        public void run() { processAndHandOff(); }
    }
}

 (3)多Reactor多线程模式

Netty入门(五) Netty原理之Reactor模式_第4张图片

对于多核CPU,为了充分利用CPU的性能,引入了多Reactor,也即一个主Reactor负责监控所有的连接请求,多个子Reactor负责监控并处理读/写请求,减轻了主Reactor的压力,降低了主Reactor压力太大而造成的延迟。并且每个子Reactor分别属于一个独立的线程,每个成功连接后的Channel的所有操作由同一个线程处理。这样保证了同一请求的所有状态和上下文在同一个线程中,避免了不必要的上下文切换,同时也方便了监控请求响应状态。Netty就是利用了该Reactor模式去搭建的,代码如下:

Selector[] selectors; //subReactors集合, 一个selector代表一个subReactor
int next = 0;
class Acceptor { // ...
    public synchronized void run() { ...
        Socket connection = serverSocket.accept(); //主selector负责accept
        if (connection != null)
            new Handler(selectors[next], connection); //选个subReactor去负责接收到的connection
        if (++next == selectors.length) next = 0;
    }
}


 

你可能感兴趣的:(Netty入门(五) Netty原理之Reactor模式)