Reactor线程模型

1. 概念

一种并发编程模型, 是一种思想, 也叫1+M+N线程模式, 应用广泛, 如Nginx、Memcached、Netty等

2.角色

Reactor: 负责监听和分配事件, 将IO事件分派给对应的Handler, 新的事件包括连接建立就绪、读就绪、写就绪等
Acceptor: 处理客户端新连接, 并分派请求到处理器中
Handler: 将自身与事件绑定, 执行非阻塞IO任务, 完成channel读写, 一级业务逻辑

3.模型类型

单Reactor-单线程

在这里插入图片描述
缺点: 所有接受连接, 处理数据操作都在一个线程中完成, 有性能瓶颈
在这里插入图片描述
缺点: 将比较耗时的数据编码、解码、计算等操作放入线程池中执行, 虽然提升性能但还不是最好的方式

单Reactor-多线程

在这里插入图片描述

⭐主从Reactor-多线程

主从多线程, 对于服务器来说, 接收客户端的链接单独用线程操作
在这里插入图片描述
工作流程:

  1. Reactor主线程MainReactor通过select监听客户端连接时间, 接收到事件后, 通过Acceptor处理客户端链接事件
  2. 当Acceptor处理完成链接事件后, MainReactor将连接分配到SubReactor
  3. SubReactor将连接加入到自己的连接队列进行监听, 并创建Handler各种事件进行处理
  4. 当连接上有新事件发生, Subreactor就会调用对应的Handler处理
  5. Handler通过read从通道上获取数据, 将请求分发到Worker线程池处理业务
  6. Worker线程池会分配独立线程完成业务处理, 并将处理请求返回给Handler, Handler通过send向客户端响应数据
  7. 一个MainReactor可以对应多个SubReactor, 即一个MainReactor线程对应多个SubReactor线程

Code design(参考zk)

1. 创建服务, 创建服务上下文工厂
Server server = new Server();
CountDownLatch latch = new CountDownLatch(1);
server.registerShutDownHandler(new ServerShutDownHandler(latch));

// 创建连接管理器工厂
if (config.getClientPortAddress() != null) {
    cnxnFactory = ServerCnxnFactory.createFactory();
    // 初始化配置
    cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns(), config.getClientPortListenBacklog(), false);
2. 初始化服务上下文工厂配置
// 初始化最大连接数
this.maxClientCnxns = maxClientCnxns;
initMaxCnxns();

// 准备相关资源 获取cpu核数
int coreNum = Runtime.getRuntime().availableProcessors();
// 计算用于检测客户端IO事件的线程数量
numSelectorThreads = Integer.getInteger(NIO_NUM_SELECTOR_THREADS,
        Math.max((int) Math.sqrt((double) coreNum / 2), 1));
// 计算用于处理客户端IO事件的线程数量
numWorkerThreads = Integer.getInteger(NIO_NUM_WORKER_THREADS, 2 * coreNum);

//准备好SelectThread
for (int i = 0; i < numSelectorThreads; i++) {
    selectorThreads.add(new SelectorThread(i));
}
// 打开一个服务端的ServerSocketChannel
this.ss = ServerSocketChannel.open();
this.ss.configureBlocking(false);
// 关闭立即释放端口
ss.socket().setReuseAddress(true);
if ((listenBacklog = clientPortListenBacklog) == -1) {
    ss.socket().bind(clientPortAddress);
} else {
    ss.socket().bind(clientPortAddress, listenBacklog);
}
//创建AcceptThread
acceptThread = new AcceptThread(this.ss, selectorThreads, clientPortAddress);

创建固定长度Selector IO检测线程, 创建ServerSocketChannel, listenBacklog请求队列大小, 创建accept线程

3. 启动服务, 从里往外(worker > selector > accept)依次进行启动线程
// 初始化配置
cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns(), config.getClientPortListenBacklog(), false);
//启动连接管理器
cnxnFactory.startup(server);
// 服务停止标识
stopped = false;  
// 启动IO工作线程池
if (workerPool == null) {
    workerPool = new IOWorkerService(numWorkerThreads, "IOWorker");
}

// 启动selector thread
for (SelectorThread selectorThread : selectorThreads) {
    if (selectorThread.getState() == Thread.State.NEW) {
        selectorThread.start();
    }
}
// 启动accept thread
if (acceptThread.getState() == Thread.State.NEW) {
    acceptThread.start();
}
4. Accept Thread中处理客户端请求连接
/**
 * AcceptThread 检测是否有新连接
 */
@Override
public void run() {
    try {
        while (!stopped && !serverSocketChannel.socket().isClosed()) {
            //执行select程序
            select();
        }
    } finally {
        closeSelector();
    }
}

private void select() {
    try {
        selector.select();
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        while (!stopped && iterator.hasNext()) {
            SelectionKey key = iterator.next();
            iterator.remove();

            if (!key.isValid()) {
                continue;
            }
            // 判断是否是accept
            if (key.isAcceptable()) {
                // 接收连接
                if (!doAccept()) {
                    pauseAccept(10);
                }
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

/**
 * 接收新连接,并绑定到一个selector thread
 *
 * @return
 */
private boolean doAccept() {
    boolean accepted = false;
    SocketChannel socketChannel = null;
    try {
        socketChannel = serverSocketChannel.accept();
        accepted = true;

        // TODO  cnxn over limit

        // 设置非阻塞
        socketChannel.configureBlocking(false);
        // 将当前连接 绑定给一个 selector thread round robin
        if (!selectorIterator.hasNext()) {
            selectorIterator = selectorThreads.iterator();
        }
        SelectorThread selectorThread = selectorIterator.next();
        if (!selectorThread.addAcceptedConnection(socketChannel)) {
            throw new IOException("unable to add connection to selector thread");
        }
    } catch (IOException e) {
        e.printStackTrace();
        fastCloseSock(socketChannel);
    }
    return accepted;
}

doAccept为主要处理连接接收代码, selector接收到客户端请求, 接收到一个SocketChannel, 将其添加到selecor thread(IO检测线程)中, 其中获取selecor thread时采用迭代器实现轮询(round robin)的效果, 目的将SocketChannel平均分配到selecor thread的队列中

5.SelectorThread中检测IO事件
@Override
public void run() {
    while (!stopped) {
        try {
            // IO select
            select();
            // 处理新接收的连接
            processAcceptedConnections();

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

    //
    // Close connections still pending on the selector. Any others
    // with in-flight work, let drain out of the work queue.

    for (SelectionKey key : selector.keys()) {
        NIOServerCnxn cnxn = (NIOServerCnxn) key.attachment();
        if (cnxn.isSelectable()) {
            cnxn.close(ServerCnxn.DisconnectReason.SERVER_SHUTDOWN);
        }
        cleanupSelectionKey(key);
    }
    SocketChannel accepted;
    while ((accepted = acceptedQueue.poll()) != null) {
        fastCloseSock(accepted);
    }
}

private void select() {
    try {
        selector.select();
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        while (!stopped && iterator.hasNext()) {
            SelectionKey key = iterator.next();
            iterator.remove();

            if (!key.isValid()) {
                cleanupSelectionKey(key);
                continue;
            }

            //检测是否有 IO
            if (key.isReadable() || key.isWritable()) {
                handleIO(key);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private void processAcceptedConnections() {
    SocketChannel sc;
    while (!stopped && (sc = acceptedQueue.poll()) != null) {
        SelectionKey key = null;
        try {
            key = sc.register(selector, SelectionKey.OP_READ);
            // 封装连接
            NIOServerCnxn cnxn = createConnection(sc, key, this);
            key.attach(cnxn);

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

            cleanupSelectionKey(key);
            fastCloseSock(sc);
        }
    }
}

// 包装 selectionKey, 提交给IOWorkPool
private void handleIO(SelectionKey key) {
    // 封装 IOWorkRequest
    IOWorkRequest ioWorkRequest = new IOWorkRequest(this, key);

    NIOServerCnxn cnxn = (NIOServerCnxn) key.attachment();

    // Stop selecting this key while processing on its connection
    cnxn.disableSelectable();
    // IO处理是在异步线程中,Select线程会不断select该key ,在当前IO未完成前暂停检测
    key.interestOps(0);

    //交给 workerPool
    workerPool.schedule(ioWorkRequest);
}

select轮序检查有无新的读写事件, processAcceptedConnections处理新连接的时间绑定, 在select中遍历获取到的SelectionKey, 进行IO操作, 主要实现在handleIO中; 注意的是IO处理是在异步线程中, Select线程会不断select该key, 可以使用interestOps方法标记SelectionKey没有任何兴趣操作, selector.select()时就不会检测到该SelectionKey, 最终将上下文信息封装到IOWorkRequest进行工作线程的提交

6.Worker线程处理IO时间
// 处理 IOWork
public void schedule(IOWork ioWork) {
    if (stopped) {
        ioWork.cleanup();
        return;
    }
    // 将IOWork 包装成一个handler
    IOWorkHandler ioWorkHandler = new IOWorkHandler(ioWork);
    if (ioWorkerPool != null) {
        try {
            ioWorkerPool.execute(ioWorkHandler);
        } catch (RejectedExecutionException e) {
            e.printStackTrace();
            ioWork.cleanup();
        }
    } else {
        ioWorkHandler.run();
    }
}

将ioWork封装到IOWorkHandler中提交事务, IOWorkHandler.run()->IOWork.doWork()

// 真正处理 IO
@Override
public void doWork() throws Exception {
    if (!key.isValid()) {
        selectorThread.cleanupSelectionKey(key);
        return;
    }

    if (key.isReadable() || key.isWritable()) {
        // 执行IO操作
        cnxn.doIO(key);

        if (stopped) {
            cnxn.close(ServerCnxn.DisconnectReason.SERVER_SHUTDOWN);
            return;
        }
        if (!key.isValid()) {
            selectorThread.cleanupSelectionKey(key);
            return;
        }
    }

    // Mark this connection as once again ready for selection
    cnxn.enableSelectable();
    cnxn.requestInterestOpsUpdate();
}
/**
 * Handles read/write IO on connection.
 */
public void doIO(SelectionKey key) {
    try {
        if (!isSocketOpen()) {
            return;
        }

        //处理读事件
        if (key.isReadable()) {
            int i = sc.read(incomingBuffer);
            if (i < 0) {
                handleFailedRead();
            }

            // 等到 incomingBuffer 读满
            if (incomingBuffer.remaining() == 0) {

                boolean isBody = false;

                // 如果header 读满
                if (incomingBuffer == headerBuffer) {
                    incomingBuffer.flip();
                    // 读 body 的长度
                    isBody = readHeader(key);
                    incomingBuffer.clear();
                } else {
                    isBody = true;
                }

                // 如果 body 读满
                if (isBody) {
                    readBody();
                } else {
                    return;
                }

            }
        }

        //处理可写事件
        if (key.isWritable()) {
            handleWrite(key);
        }
    } catch (EndOfStreamException e) {
        e.printStackTrace();

        close(e.getReason());

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

        close(DisconnectReason.IO_EXCEPTION);
    }

}

获取客户端数据, readBody()>doRequest()>processPacket(), 讲读到的数据封装到BusinessHandler中, 提交业务线程

 /**
  * 服务端处理数据
  *
  * @param cnxn
  * @param incomingBuffer
  */
 public void processPacket(ServerCnxn cnxn, ByteBuffer incomingBuffer) {
     // 封装 request
     Request request = new Request(cnxn, incomingBuffer);
     // TODO throttled
     // submit request
     businessService.submitRequest(request);
 }
/**
 * 提交请求
 *
 * @param request
 */
public void submitRequest(Request request) {
    BusinessHandler handler = new BusinessHandler(request);
    executorService.execute(handler);
}

public class BusinessHandler implements Runnable {

    private final ServerCnxn cnxn;

    private final Request request;

    public BusinessHandler(Request request) {
        this.request = request;
        this.cnxn = request.getCnxn();
    }

    @Override
    public void run() {
        try {
            Serializer serializer = server.getSerializer();

            ByteBuffer reqBuf = this.request.getRequest();
            byte[] bytes = new byte[reqBuf.remaining()];
            reqBuf.get(bytes);
            RequestData requestData = null;
            if (serializer instanceof RequestSerializer) {
                RequestSerializer requestSerializer = (RequestSerializer) serializer;
                requestData = requestSerializer.decodeRequest(bytes);
            }
            System.out.println("服务端接收到的请求数据为:" + requestData);
            SecureRandom secureRandom = new SecureRandom();
            int i = secureRandom.nextInt(2000);
            System.out.println("即将模拟请求执行耗费" + i + "毫秒");
            Thread.currentThread().sleep(i);

            //回写数据
            ResponseData response = new ResponseData();
            response.setId(requestData.getId());
            response.setStatus(200);
            response.setMsg("hello " + i);
            System.out.println("服务端要返回的响应为:" + response);

            if (serializer instanceof ResponseSerializer) {
                ResponseSerializer rs = (ResponseSerializer) serializer;
                byte[] rsbytes = rs.encodeResponse(response);
                ByteBuffer buffer = ByteBuffer.allocate(4 + rsbytes.length);
                buffer.putInt(rsbytes.length);
                buffer.put(rsbytes);
                buffer.flip();
                //发送buffer
                cnxn.sendBuffer(buffer);

                // TODO throttled
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在业务线程BusinessHandler中, 反序列化接收到的数据, 处理业务逻辑, 最终在上下文中写出响应数据
ServerCnxn.sendBuffer() > requestInterestOpsUpdate() > SelectorThread.addInterestOpsUpdateRequest() 最终将要写出的数据写入到IO检测线程的update队列中, 在执行一遍handlerIO操作, 将数据写回到客户端

线程比例1(accept thread) : M(selector thread): N(busness thread)
变种reactor线程比例1(accept thread) : M(selector thread) : N(ioWorker thread) : K(busness thread)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Allen-xs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值