Reactor线程模型

本文详细探讨了服务器端的线程模型,包括阻塞模型、Reactor模型(单线程、多线程及主从结构)和Proactor模式,介绍了它们的处理机制、优缺点及代码示例,强调了在不同场景下的选择和优化策略。
摘要由CSDN通过智能技术生成


一、背景

服务器需要等待客户端请求,在进行业务处理之后,将结果返回到客户端。其中服务器应用通过内核与网卡进行数据交互,具体原理和交互方式已在上一篇网络通信与IO多路复用中进行了详细介绍。

1.socket网络通信

客户端与服务器进行通信需要经过建立连接、请求、返回、关闭连接等步骤,而服务器需要监听客户端发起的创建连接、读取数据、业务处理、返回数据、关闭连接等步骤。
在这里插入图片描述

2.IO模型与线程模型

  • IO模型
    就是应用与内核读取socket数据的交互方式,已经在上一篇网络通信与IO多路复用中进行了详细介绍。
  • 线程模型
    服务器利用线程对从监听、创建连接、业务处理、关闭连接整个过程的处理方式。

3.线程模型分类

服务器处理一个网络请求需要经过以下步骤:

  • read:从socket读取数据
  • decode:解码,将网络上的流式byte转化成请求
  • compute:计算,主要是进行业务处理
  • encode:编码,将请求转化成流式byte数据

线程模型主要分为以下三类:

3.1 阻塞模型

服务端为每个客户端建立线程进行以上处理,一个线程为一个客户端服务。

3.2 Reactor模型

基于IO模型中的IO多路复用,服务端一个线程为多个客户端服务。

3.3 Proactor模式

基于IO模型中的异步IO,服务端一个线程为多个客户端服务。

二、阻塞模型

阻塞模型是针对每个客户端都会开启一个线程进行读事件处理以及业务处理。
在这里插入图片描述

1.代码示例

Reactor代码:

public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(9696);
            Socket socket = serverSocket.accept();
            new Thread(() -> {
                try {
                    byte[] byteRead = new byte[1024];
                    socket.getInputStream().read(byteRead);
 
                    String req = new String(byteRead, StandardCharsets.UTF_8);//encode
                    // do something
 
                    byte[] byteWrite = "Hello".getBytes(StandardCharsets.UTF_8);//decode
                    socket.getOutputStream().write(byteWrite);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

三、Reactor模型

Reactor模型主要是利用IO多路复用,利用少量线程为处理所有客户端的连接请求和读写请求。主要包含一下几部份:

  • Reactor:利用IO多路复用,监控连接请求就绪和读写请求就绪
  • Acceptor:接受客户端连接请求并创建连接,然后注册到Reactor的多路复用Selector中
  • Handle:对客户端的写请求进行读写编码和业务处理

1.单Reactor单线程

在这里插入图片描述

1.1 处理过程

  • Reactor构建Selector,监控port的连接事件并注册到Selector,并添加连接事件处理方法Acceptor
  • 有连接请求后,会触发Acceptor逻辑,会创建socketChannel并将此socketChannel的读事件注册到Reactor的Selector,并添加读事件处理方法Handle
  • 如果有读请求,会触发Handle事件,进行业务处理

1.2 优缺点

一个线程需要处理连接请求、读写请求、业务处理,处理业务请求会想对较慢。需要等待业务处理完成,才能对其他请求进行处理。
单线程Reactor模型编程比较简单,比较适合业务处理可以快速完成的场景,比如Redis。

1.3 代码示例

Reactor代码:

static class Reactor implements Runnable {
        ServerSocketChannel serverSocketChannel;
        Selector selector;

        public Reactor(int port) {
            try {
                serverSocketChannel = ServerSocketChannel.open();
                selector = Selector.open();
                // 监听port端口
                serverSocketChannel.socket().bind(new InetSocketAddress(port));
                serverSocketChannel.configureBlocking(false);
                // 注册连接事件
                SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_CONNECT);
                // 在连接事件上附加一个处理类
                selectionKey.attach(new Acceptor(selector, serverSocketChannel));
            } catch (IOException e) {
                System.out.println(e);
            }
        }

        @Override
        public void run() {
            while (true) {
                try {
                    selector.select();
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    while (iterator.hasNext()) {
                        SelectionKey selectionKey = iterator.next();
                        dispatcher(selectionKey);
                        iterator.remove();
                    }
                } catch (Exception e) {

                }
            }
        }

        private void dispatcher(SelectionKey selectionKey) {
            Runnable runnable = (Runnable) selectionKey.attachment();
            runnable.run();
        }
    }

Acceptor与WorkHandle代码:

static class Acceptor implements Runnable {
        Selector selector;
        ServerSocketChannel serverSocketChannel;

        public Acceptor(Selector selector, ServerSocketChannel serverSocketChannel) {
            this.selector = selector;
            this.serverSocketChannel = serverSocketChannel;
        }

        @Override
        public void run() {
            try {
                SocketChannel socketChannel = serverSocketChannel.accept();
                SelectionKey selectionKey = socketChannel.register(selector, SelectionKey.OP_READ);
                selectionKey.attach(new WorkHandle(socketChannel));
            } catch (Exception e) {

            }
        }
    }

    static class WorkHandle implements Runnable {
        SocketChannel socketChannel;

        public WorkHandle(SocketChannel socketChannel) {
            this.socketChannel = socketChannel;
        }

        @Override
        public void run() {
            try {
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                socketChannel.read(byteBuffer);

                socketChannel.write(ByteBuffer.wrap("message received".getBytes(StandardCharsets.UTF_8)));
            } catch (Exception e) {

            }
        }
    }

2.单Reactor多线程

单Reactor多线程主要是将业务处理进行多线程处理,其他操作还是由单线程处理。
在这里插入图片描述

2.1 处理机制

从整体架构图可知,整个处理机制与Reactor单线程模式基本一致。单个线程还是需要负责处理连接和读写请求,但是业务处理会提交到线程池进行处理。

2.2 优缺点

此模型不会因为业务处理相对较慢,而导致IO请求出现等待的情况。但是由于Reactor既要负责连接请求还需要负责读写请求。一个客户端一般只有一次连接请求,但是会有多次读写请求。如果有频繁的读请求,单个线程处理可能会导致无法及时处理其他读请求。

3.主从Reactor

主从Reactor模型主要是利用一个主Reactor监控连接请求就绪、一个或者多个子Reactor监控读请求就绪,业务请求还是通过线程池处理。
在这里插入图片描述

3.1 处理机制

从整体架构图可知,整个处理机制与Reactor多线程模式基本一致。单个线程负责处理连接请求,一个或者多个线程负责处理读写请求,业务处理会提交到线程池进行处理。

3.2 优缺点

此模型逻辑处理相对比较复杂。但是由于将连接请求还需要读写请求进行了拆分处理,可以很好的支持频繁的读写请求场景。


参考链接

1.Reactor模型

  • 11
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值