Reactor模式详解

对于传统的c/s服务模式,当客户端有请求到达服务端,服务端会有对应的线程去处理数据间的交互。当客户端请求数量很大的时候,服务端会有很多的线程来处理,此时线程间的切换会耗费巨大的资源,同时线程本身也会消耗很多的资源。当连接没有数据传输的时候,线程仍然需要监听连接,此时也会造成资源的无效浪费。因此,reactor模式可以很好的来解决出现的这些问题。

reactor中的5种角色

reactor: An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events

  1. Handle
    handle称为句柄或是描述符,本质上表示一种资源,是由操作系统提供的。该资源用于表示一个个的事件,比如说文件描述符,针对网络变成中的Socket描述符。事件既可以来自外部,也可以来自于内部。外部事件比如说客户端的连接请求等;内部事件比如说操作系统产生的定时器事件等。它本质上就是一个文件描述符(在linux系统中一切都是文件)。Handle是事件产生的发源地

  2. Sysnchronous Event Demultiplexer
    Sysnchronous Event Demultiplexer称为同步事件分离器,它本身是由系统调用用于等待事件的发生(事件可能是一个,也可能是多个)。调用方在调用它的时候会被阻塞,直到同步事件分离器上有事件产生为止。对于Linux来说,同步事件分离器指的就是常用的I/O多路复用机制,比如说select、epoll、epoll等。在Java nio领域中,同步事件分离器对应的组件就是Selector,对应的阻塞方法就是select方法。

  3. Event Handler
    Event Handler称为事件处理器,由多个回调方法构成,这些回调方法构成了与应用相关的对于某个事件的反馈机制。在Java nio领域中,是没有这一个角色的,某个事件产生,我们在代码中自己判断事件类型并且去处理相应的逻辑。Netty相对于Java nio来说,在事件处理器这个角色上进行来一个升级,它为我们开发者提供了大量的回调方法,供我们在特定事件产生时实现相应的回调方法进行业务逻辑的处理。例如ChannelInboundHandlerAdapter类,定义来一系列回调的方法供我们在相应事件发生的时候来调用。

  4. Concrete Event Handler
    Concrete Event Handler具体事件处理器,是事件处理器的实现。实现了事件处理器所提供的各个回调方法,从而实现来特定于业务的逻辑。本质就是我们所编写的一个个的处理器实现。


public abstract class SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter {
...
    protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;
...
}


public class MyServerHandler extends SimpleChannelInboundHandler<String>{
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println(ctx.channel().remoteAddress() + "," + msg);
        ctx.channel().writeAndFlush("from server: " + UUID.randomUUID());

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

MyServerHandler就是具体的事件处理器。实现了channelRead0方法,该方法定义于父类也就是事件处理器中。
5. Initiation Dispatcher
Initiation Dispatcher称为初始化分发器,实际上就是Reactor角色。定义来一些规范,这些规范用于控制事件的调度方式(Initiation Dispatcher拿到select返回的selectionkey结果集),同时又提供了应用进行事件处理器的注册、删除等设施(下图中的register_handler(h)/remove_handler(h))。此组件是整个事件处理器的核心所在,Initiation Dispatcher会通过同步事件分离器来等待事件的发生,一旦事件发生来,Initiation Dispatcher首先分离出每一个事件(遍历selectionkey结果集,每一个元素就是一个事件),然后调用事件处理器,最后会调用相关的回调方法来处理这些事件。

参考以下权威的两张图:
这里写图片描述

这里写图片描述


Reactor模式的流程

  1. 当应用向Initiation Dispatcher注册Concrete Event Handler时,应用会标识出该事件处理器希望Initiation Dispatcher在某个事件发生时向其通知的该事件,该事件与Handle关联
  2. Initiation Dispatcher会要求每个事件处理器向其传递内部的Handle。该Handle向操作系统标识了事件处理器
  3. 当所有的事件处理器注册完毕后,应用会调用handle_events方法来启动Initiation Dispatcher的事件循环。这时,Initiation Dispatcher会将每个注册的事件管理器的Handle合并起来(放入一个集合),并使用同步事件分离器等待这些事件的发生。比如说,TCP协议层会使用select同步事件分离器操作来等待客户端发送的数据到达连接过的socket handle上
  4. 当与某个事件对应的Handle变为ready状态时(比如说,TCP socket变为等待读状态时),Synchronous Event Demultiplexer就会通知Initiation Dispatcher
  5. Initiation Dispatcher会触发事件处理器的回调方法(channelRead0,channelActive…接口层面非concrete event handler),从而响应这个处于ready状态的handle。当事件发生时,Initiation Dispatcher会将被事件源激活的Handle作为【key】来寻找并分发恰当的事件处理器回调方法(因为之前是将所有的handle都注册到Initiation Dispatcher,所以需要筛选)
  6. Initiation Dispatcher会回调事件处理器的handle_events回调方法来执行特定与应用的功能(开发者自己所编写的功能),从而响应这个事件。所发生的事件类型可以作为该方法的参数并被方法内部用来执行额外的特定与 服务的分离与分发(例如一个socket handle文件描述符,可以有读或者写对应的事件类型被处理)。

上述两个图的一些差异。在netty中,有对应的mainReactor(BossGroup)和subReactor(WorkGroup),两者就是Initiation Dispatcher。mainReactor只会接受连接,并传递给subReactor,因此中间的acceptor就是启动连接的作用。netty在服务器绑定到端口号上的时候,会向mainReactor中添加一个handler也就是一个ChannelInitializer(一个inbound类型的handler),在这里新建一个ServerBootstrapAcceptor对象,每次绑定新的端口都会产生这个组件。源码中也就是添加这个handler到pipeline中的最后位置。当acceptor接受到客户端传递的数据之后,会在channelRead方法中将原客户端的channel绑定到subReactor(io线程组)中。

void init(Channel channel) throws Exception {
        ...

        ChannelPipeline p = channel.pipeline();
        ...

        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

ServerBootstrapAcceptor类中:

public void channelRead(ChannelHandlerContext ctx, Object msg) {
            final Channel child = (Channel) msg;

            child.pipeline().addLast(childHandler);

            setChannelOptions(child, childOptions, logger);

            for (Entry<AttributeKey<?>, Object> e: childAttrs) {
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }

            try {
                childGroup.register(child).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
                forceClose(child, t);
            }
        }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值