Reactor模式与Netty

在netty的线程模型中,bossGroup只负责请求的转发,workerGroup是具体的数据处理,其实netty使用的是Reactor(响应器)的设计模式。

一篇文章对这种模式做了非常细致的介绍,《Scalable IO in Java》 ,这篇文章的作者是 Doug Lea!!!

Reactor模式与Netty_第1张图片
大多数的网络服务都是下面的流程:

  • 读取请求
  • 解码
  • 业务逻辑处理
  • 编码响应
  • 发送响应

典型的设计实现如下:
Reactor模式与Netty_第2张图片
每个请求的处理都在自己的线程里。

Reactor模式与Netty_第3张图片
采用分治是应对拓展的好方法:
将一个处理过程分成多个小任务,每个任务都是非阻塞的;
当任务可执行时再调用;下图中IO事件通常充作触发开关
Reactor模式与Netty_第4张图片
底层利用java.nio 的非阻塞的读与写操作,一个Selector管理多个channel。
来看看Reactor是怎么做的。

Reactor 模式

Reactor 单线程模型

Reactor模式与Netty_第5张图片
使用异步非阻塞 I/O,一个线程独立处理所有 I/O相关操作。通过 Acceptor 类接收客户端连接请求,建立链路,dispatch 将对应的信息 ByteBuffer 分发到响应 handler,进而对消息进行解析处理。

通过代码来理解:

class Reactor implements Runnable {
    final Selector selector;
    final ServerSocketChannel serverSocket;
    
	Reactor(int port) throws IOException {
    	selector = Selector.open();
    	serverSocket = ServerSocketChannel.open();
    	serverSocket.socket().bind(new InetSocketAddress(port));
    	serverSocket.configureBlocking(false);
    	SelectionKey sk =serverSocket.register(selector,SelectionKey.OP_ACCEPT);
    	sk.attach(new Acceptor());
	}
	/*
	Alternatively, use explicit SPI provider:
	SelectorProvider p = SelectorProvider.provider();
	selector = p.openSelector();
	serverSocket = p.openServerSocketChannel();
	*/
	// class Reactor continued
	public void run() { // normally in a newThread
    	try {
        	while (!Thread.interrupted()) {
            	selector.select();
            	Set selected = selector.selectedKeys();
            	Iterator it = selected.iterator();
            	while (it.hasNext()) {
            		dispatch((SelectionKey)(it.next());
            	}
            	selected.clear();
        	}
    	} catch (IOException ex) { /* ... */ }
    	}
	}   
	void dispatch(SelectionKey k) {
    	Runnable r = (Runnable)(k.attachment());
        if (r != null){
        	r.run();
        }
	}

Reactor 负责监听连接请求,初始化时绑定一个Acceptor,连接到来后遍历selectedKeys,调用dispatch分发请求,在dispatch里边通过selectedKey得到绑定的Acceptor,调用其run方法
看一下Acceptor :

// class Reactor continued
class Acceptor implements Runnable { // inner
    public void run() {
        try {
        SocketChannel c = serverSocket.accept();
        if (c != null)
        new Handler(selector, c);
        }catch(IOException ex) { /* ... */ }
        }
    }
}

构造了一个SocketChannel与该客户端通信,创建了处理该请求的handler。

Handler有SocketChannel 和SelectionKey的引用,Handler的构造器将当前类(Handler)加入到绑定里边,并且对READ感兴趣,之后调sel.wakeup()意思是让select( )方法立刻返回,如果当前没有select()方法阻塞的话,那么下一次调用select()会立即返回,然后执行run()方法,是通过判断状态的方式来决定是写还是读 ,这个在Netty3中就是需要这样实现handler代码的,需要自己判断状态来决定业务逻辑。Netty4已经改成各种回调了,比如channelRead,channelActive等。

Handler:

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);
        sk.interestOps(SelectionKey.OP_READ);
        sel.wakeup();//注册OP_READ兴趣之后,让select()方法返回,接受要读取的数据
    }
    boolean inputIsComplete() { /* ... */ }
    boolean outputIsComplete() { /* ... */ }
    void process() { /* ... */ }

// class Handler continued
    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);//将状态变为SENDING之后,接下来就是往外写数据,对写感兴趣。
        }
    }
    void send() throws IOException {
        socket.write(output);
        if (outputIsComplete()) sk.cancel();
        }
    }

单线程模型适合一些小容量场景,无法应对高负载,高并发需求。

Reactor 多线程模型

handler 优化,引入handler 线程池,也就是使用一组线程来处理 I/O操作。
Reactor模式与Netty_第6张图片
有一个专门监听客户端连接请求的 Acceptor 线程;线程池包含多个可用线程,由这些线程来负责消息的处理;一个线程可以同时处理多条链路,但一条链路只对应一个线程。

Reactor模式与Netty_第7张图片

主从 Reactor 多线程模型:Reactor模式与Netty_第8张图片

服务端用于接收客户端的连接是一个独立的线程池 mainReactor,Acceptor 接收到客户端的链接请求处理后,将新创建的SocketChannel 注册到 subReactor 线程池的某个线程上,由他来负责 SocketChannel。这也是Netty 推荐使用的线程模型。

Netty的线程模型:

Netty同时支持多种线程模型,你可以根据需要来配置。
Reactor模式与Netty_第9张图片
图片来自《Netty权威指南》
这张图结合源码看会由更好的理解,Netty源码解析-NioEventLoop

你可能感兴趣的:(Netty,Netty源码解析)