Reactor反应器模式
  lh6O4DgR0ZQ8 2023年11月02日 53 0

在Java的OIO编程中,最初和最原始的网络服务器程序使用一个while循环,不断地监听端口是否有新的连接,如果有就调用一个处理函数来处理。这种方法最大的问题就是如果前一个网络连接的处理没有结束,那么后面的连接请求没法被接收,于是后面的请求统统会被阻塞住,服务器的吞吐量就太低了。 为了解决这个严重的连接阻塞问题,出现了一个即为经典模式:Connection Per Thread。即对于每一个新的网络连接都分配一个线程,每个线程都独自处理自己负责的输入和输出,任何socket连接的输入和输出处理不会阻塞到后面新socket连接的监听和建立。早期版本的Tomcat服务器就是这样实现的。

这种模式的优点是解决了前面的新连接被严重阻塞的问题,在一定程度上极大地提高了服务器的吞吐量。但是对于大量的连接,需要消耗大量的现成资源,如果线程数太多,系统无法承受。而且线程的反复创建、销毁、线程的切换也需要代价。因此高并发应用场景下多线程OIO的缺陷是致命的,因此引入了Reactor反应器模式。

反应器模式由Reactor反应器线程、Handlers处理器两大角色组成:

Reactor反应器线程的职责:负责响应IO事件,并且分发到Handlers处理器 Handlers处理器的职责:非阻塞的执行业务处理逻辑 一、单线程Reactor反应器模式 Reactor反应器模式有点儿类似事件驱动模式,当有事件触发时,事件源会将事件dispatch分发到handler处理器进行事件处理。反应器模式中的反应器角色类似于事件驱动模式中的dispatcher事件分发器角色。

Reactor反应器:负责查询IO事件,当检测到一个IO时间,将其发送给对应的Handler处理器处理,这里的IO事件就是NIO选择器监控的通道IO事件。 Handler处理器:与IO事件绑定,负责IO事件的处理,完成真正的连接建立、通道的读取、处理业务逻辑、负责将结果写出到通道等。 基于NIO实现单线程版本的反应器模式需要用到SelectionKey选择键的几个重要的成员方法:

void attach(Object o):将任何的Java对象作为附件添加到SelectionKey实例,主要是将Handler处理器实例作为附件添加到SelectionKey实例 Object attachment():取出之前通过attach添加到SelectionKey选择键实例的附件,一般用于取出绑定的Handler处理器实例。 Reactor实现示例:

package cn.ken.jredis;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Set;

/**
 * <pre>
 *
 * </pre>
 *
 * @author <a href="https://github.com/Ken-Chy129">Ken-Chy129</a>
 * @since 2023/10/14 14:29
 */
public class Reactor implements Runnable {

    final private Selector selector;

    final private ServerSocketChannel serverSocketChannel;

    public Reactor() {
        try {
            this.selector = Selector.open();
            this.serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(8088));
            // 注册ServerSocket的accept事件
            SelectionKey sk = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            // 为事件绑定处理器
            sk.attach(new AcceptHandler());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                for (SelectionKey selectedKey : selectionKeys) {
                    dispatch(selectedKey);
                }
                selectionKeys.clear();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void dispatch(SelectionKey selectedKey) {
        Runnable handler = (Runnable) selectedKey.attachment();
        // 此处返回的可能是AcceptHandler也可能是IOHandler
        handler.run();
    }

    class AcceptHandler implements Runnable {
        @Override
        public void run() {
            try {
                SocketChannel socketChannel = serverSocketChannel.accept();
                if (socketChannel != null) {
                    new IOHandler(selector, socketChannel); // 注册IO处理器,并将连接加入select列表
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) {
        new Reactor().run();
    }
}

这个代码使用内置的Node.js http模块来创建一个HTTP服务器。这个服务器使用createServer方法初始化创建,接着无论何时服务器收到请求,都调用一个传递给它的回调函数。

在回调方法中,我们告诉客户端响应是"text/plain"类型,文本为"Hello, Node.js!",状态码是200。

当这一切都准备好了,我们使用监听方法发布到服务器,并且告诉它开始在端口3000上监听。为了让管理员知道服务器已经启动并且运行,无论什么时候服务器只要起来了,终端就会打印出一个信息。

启动web浏览器,访问http://localhost:3000来测试服务器。浏览器窗口将会显示"Hello, Node.js!"。

尽管这是一个非常基础的例子,但它展示了Node.js服务器程序是如何构成的。有很多Node.js的模块构成开发特定应用的基础设施。他们是的你能够构建复杂程序。

最后的思考 无论你为搭建应用选择Spring Boot还是Node.js,这两个都是各自语言中非常强大的框架。他们很流行、构建应用也简单。两个都支持微服务架构,两个都可以用自己的方式构建微服务,并且把他们集成到应用里面。不管你如何使用它们,它们始终能给你一种更好的编程方式。在当前世界中,我坚信没有比Spring Boot更加敏捷和流行的框架,也没有比Node.js在服务端实现更加高效的软件。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  DF5J4hb0hcmT   2023年12月12日   25   0   0 服务器git
  3I1N9ysrcSyk   2023年12月08日   31   0   0 javahapi数据交换
  DF5J4hb0hcmT   2023年12月07日   50   0   0 javaArthas
lh6O4DgR0ZQ8