上篇文章中写了一些NIO相关的知识以及简单的NIO实现示例,但是示例中,客户端连接以及读取、写入、处理客户端数据时都是在一个线程中,单个线程处理客户端的数据,性能会很差,而且不能充分利用服务器的性能,这篇文章主要介绍Reactor线程模型,NIO的多路复用知识,用以提供服务端的性能。

单Reactor线程模型



单Reactor线程模型,最终还是使用了一个线程,和上篇文章最后的示例基本上没啥差别,只是拆分了三个类来进行处理。

基于工作线程的Reactor线程模型



这里的线程模型,是在客户端连接后,业务数据的部分抽出来,放到线程池中处理,这样做的好处是,如果处理业务数据时间很长,也不会影响客户端的读写操作。



上面图示是简单的基于工作线程的工作模式,把业务数据处理单独抽出来,在线程池处理。

多Reactor线程模型



最终的线程模型,是多Reactor线程模型。

客户端数据的读写也是在多个线程中进行处理,充分提高了性能。

多Reactor线程模型示例

MainReactor-Thread和Acceptor代码:

   /**
* mainReactor-Thread
* 接收客户端连接,然后交给Acceptor处理
*/
class MainReactor extends Thread {
//创建一个Selector
public Selector selector; AtomicInteger integer = new AtomicInteger(0); public MainReactor() throws IOException {
selector = Selector.open();
} @Override
public void run() {
while (true) {
try {
//启动Selector
selector.select();
//获取事件
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
//遍历事件
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isAcceptable()) {
//获取客户端通道
SocketChannel socketChannel = ((ServerSocketChannel)selectionKey.channel()).accept();
Acceptor acceptor = new Acceptor();
//把客户端通道交给Acceptor去处理
acceptor.register(socketChannel);
}
//处理完之后要移除
iterator.remove();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} public void register(ServerSocketChannel serverSocketChannel) throws ClosedChannelException {
//注册OP_ACCEPT事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
} /**
* 处理客户端的连接
* 给每个客户端连接分配一个subReactor-Thread
*/
class Acceptor { public void register(SocketChannel socketChannel) throws IOException {
//设置为非阻塞模式
socketChannel.configureBlocking(false);
int index = integer.getAndIncrement() % subReactors.length;
SubReactor subReactor = subReactors[index];
//给客户端连接分配一个subReactor线程
subReactor.register(socketChannel);
//启动subReactor线程
subReactor.start();
System.out.println("收到新连接:" + socketChannel.getRemoteAddress()); }
}
}

SubReactor-Threads代码:

    /**
* 一个线程负责多个客户端连接
* 从channel中读数据、写数据
*/
class SubReactor extends Thread { //创建一个Selector
public Selector selector;
//用于判断SubReactor线程是否已经启动
public volatile boolean isRunning = false; public SubReactor() throws IOException {
selector = Selector.open();
} @Override
public void start() {
//判断SubReactor线程是否已经启动
//如果没有启动,就启动SubReactor线程
if (!isRunning) {
isRunning = true;
super.start();
}
} @Override
public void run() {
while (isRunning) {
try {
//启动Selector
selector.select();
//获取事件
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
//遍历事件
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isReadable()) {
try {
SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
new Handler(socketChannel);
} catch (Exception e) {
e.printStackTrace();
selectionKey.cancel();
}
}
//处理完之后要移除
iterator.remove();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} public void register(SocketChannel socketChannel) throws IOException {
//注册OP_READ事件
socketChannel.register(selector, SelectionKey.OP_READ);
} /** 读取或者写入数据 */
class Handler {
//用来读取或者写入数据
public Handler(SocketChannel socketChannel) throws IOException {
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
while (socketChannel.isOpen() && socketChannel.read(readBuffer) != -1) {
//如果有数据可读,简单的判断一下大于0
if (readBuffer.position() > 0) {
break;
}
}
//没有数据可读,就直接返回
if (readBuffer.position() == 0) {
return;
}
//转换为读取模式
readBuffer.flip();
byte[] bytes = new byte[readBuffer.limit()];
readBuffer.get(bytes);
System.out.println("获取到新的数据:" + new String(bytes));
System.out.println("获取到新的数据,来自:" + socketChannel.getRemoteAddress());
//线程池,用来处理业务数据
threadPool.execute(new Runnable() {
@Override
public void run() { }
}); //向客户端写数据
String response = "HTTP/1.1 200 OK\r\n" +
"Content-Length: 11\r\n\r\n" +
"hello world";
ByteBuffer writeBuffer = ByteBuffer.wrap(response.getBytes());
while (writeBuffer.hasRemaining()) {
socketChannel.write(writeBuffer);
}
}
}
}

初始化代码:

    /** 服务端通道 */
public ServerSocketChannel serverSocketChannel;
/** 用来接收客户端连接 */
public MainReactor mainReactor;
/** 用来处理客户端连接的读取、写入 */
public SubReactor[] subReactors = new SubReactor[10];
/** 线程池,用来处理客户端连接后的业务逻辑 */
public ExecutorService threadPool = Executors.newCachedThreadPool(); public static void main(String[] args) throws IOException {
NioReactor nioReactor = new NioReactor();
nioReactor.initAndRegister();
nioReactor.init();
nioReactor.bind();
} /** 初始化服务端 */
public void init() throws IOException {
//创建一个服务端通道
serverSocketChannel = ServerSocketChannel.open();
//设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
//注册到mainReactor-Thread
mainReactor.register(serverSocketChannel);
//启动mainReactor-Thread线程
mainReactor.start();
} /** 服务端绑定端口 */
public void bind() throws IOException {
serverSocketChannel.socket().bind(new InetSocketAddress(8056));
System.out.println("服务端启动成功");
} /** 初始化MainReactor和SubReactor */
public void initAndRegister() throws IOException {
mainReactor = new MainReactor(); for (int i=0; i<subReactors.length; i++) {
subReactors[i] = new SubReactor();
}
}

上面代码mainReactorThread和subReactorThread中有大量的重复代码,可以提取出来处理:

/** 多路复用,reactor线程模型 */
public class NioReactor2 {
abstract class ReactorThread extends Thread {
//创建一个Selector
public Selector selector;
//用于判断线程是否已经启动
public volatile boolean isRunning = false; /** 有事件发生,就调用这个方法 */
public abstract void handler(SelectableChannel channel) throws IOException; public ReactorThread() throws IOException {
selector = Selector.open();
} @Override
public void run() {
while (isRunning) {
//启动Selector
try {
selector.select();
//获取事件
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
//遍历事件
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
int readyOps = key.readyOps();
//只关注是否有OP_ACCEPT和OP_READ事件
if ((readyOps & (SelectionKey.OP_ACCEPT | SelectionKey.OP_READ)) != 0 || readyOps == 0) {
try {
//获取channel
SelectableChannel channel = key.channel();
//设置为非阻塞模式
channel.configureBlocking(false);
//有事件,就调用handler方法
handler(channel);
if (!channel.isOpen()) {
//如果channel关闭,就取消key
key.cancel();
}
} catch (Exception e) {
e.printStackTrace();
//如果有异常,就取消key
key.cancel();
}
}
//处理完之后要移除
iterator.remove();
}
selector.selectNow();
} catch (IOException e) {
e.printStackTrace();
}
}
} public SelectionKey register(SelectableChannel channel) throws ClosedChannelException {
//先注册到Selector,并没有注册任何事件
return channel.register(selector, 0);
} @Override
public void start() {
//判断SubReactor线程是否已经启动
//如果没有启动,就启动SubReactor线程
if (!isRunning) {
isRunning = true;
super.start();
}
}
} /** 服务端通道 */
public ServerSocketChannel serverSocketChannel;
/** 用来接收客户端连接 */
public ReactorThread[] mainReactors = new ReactorThread[1];;
/** 用来处理客户端连接的读取、写入 */
public ReactorThread[] subReactors = new ReactorThread[10];
/** 线程池,用来处理客户端连接后的业务逻辑 */
public ExecutorService threadPool = Executors.newCachedThreadPool(); /**
* 初始化mainReactors和subReactors
*/
public void initAndRegister() throws IOException {
//subReactors线程,用来客户端连接后的读写
for (int i=0; i<subReactors.length; i++) {
subReactors[i] = new ReactorThread() {
@Override
public void handler(SelectableChannel channel) throws IOException {
SocketChannel socketChannel = (SocketChannel)channel;
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
while (socketChannel.isOpen() && socketChannel.read(readBuffer) != -1) {
//如果有数据可读,简单的判断一下大于0
if (readBuffer.position() > 0) {
break;
}
}
//没有数据可读,就直接返回
if (readBuffer.position() == 0) {
return;
}
//转换为读取模式
readBuffer.flip();
byte[] bytes = new byte[readBuffer.limit()];
readBuffer.get(bytes);
System.out.println("获取到新的数据:" + new String(bytes));
System.out.println("获取到新的数据,来自:" + socketChannel.getRemoteAddress());
//线程池,用来处理业务数据
threadPool.execute(new Runnable() {
@Override
public void run() { }
}); //向客户端写数据
String response = "HTTP/1.1 200 OK\r\n" +
"Content-Length: 11\r\n\r\n" +
"hello world";
ByteBuffer writeBuffer = ByteBuffer.wrap(response.getBytes());
while (writeBuffer.hasRemaining()) {
socketChannel.write(writeBuffer);
}
}
};
} //mainReactors线程,用于客户端的连接
for (int i=0; i<mainReactors.length; i++) {
mainReactors[i] = new ReactorThread() {
AtomicInteger integer = new AtomicInteger(0); @Override
public void handler(SelectableChannel channel) throws IOException {
//获取客户端通道
SocketChannel socketChannel = ((ServerSocketChannel)channel).accept();
//设置为非阻塞模式
socketChannel.configureBlocking(false);
int index = integer.getAndIncrement() % subReactors.length;
ReactorThread subReactor = subReactors[index];
//启动线程
subReactor.start();
//注册事件
SelectionKey key = subReactor.register(socketChannel);
key.interestOps(SelectionKey.OP_READ);
System.out.println("收到新连接:" + socketChannel.getRemoteAddress());
}
};
}
} /** 初始化服务端 */
public void init() throws IOException {
//创建一个服务端通道
serverSocketChannel = ServerSocketChannel.open();
//设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
//注册到mainReactor-Thread
int index = new Random().nextInt(mainReactors.length);
SelectionKey keys = mainReactors[index].register(serverSocketChannel);
keys.interestOps(SelectionKey.OP_ACCEPT);
//启动mainReactor-Thread线程
mainReactors[index].start();
} /** 服务端绑定端口 */
public void bind() throws IOException {
serverSocketChannel.socket().bind(new InetSocketAddress(8056));
System.out.println("服务端启动成功");
} public static void main(String[] args) throws IOException {
NioReactor2 nioReactor = new NioReactor2();
nioReactor.initAndRegister();
nioReactor.init();
nioReactor.bind();
}
}

结束语

到此,NIO中的Reactor线程模型就结束了,上面的示例可以拆分几个类进行处理,还可以根据HTTP协议的部分,解析请求,做一个简单的tomcat服务器。

网络编程NIO之Reactor线程模型的更多相关文章

  1. 原生JDK网络编程- NIO之Reactor模式

    “反应”器名字中”反应“的由来: “反应”即“倒置”,“控制逆转”,具体事件处理程序不调用反应器,而向反应器注册一个事件处理器,表示自己对某些事件感兴趣,有时间来了,具体事件处理程序通过事件处理器对某 ...

  2. 【Netty源码分析】Reactor线程模型

    1. 背景 1.1. Java线程模型的演进 1.1.1. 单线程 时间回到十几年前,那时主流的CPU都还是单核(除了商用高性能的小机),CPU的核心频率是机器最重要的指标之一. 在Java领域当时比 ...

  3. Netty高性能之Reactor线程模型

    Netty是一个高性能.异步事件驱动的NIO框架,它提供了对TCP.UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用 ...

  4. Linux 网络编程的5种IO模型:多路复用(select/poll/epoll)

    Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 背景 我们在上一讲 Linux 网络编程的5种IO模型:阻塞IO与非阻塞IO中,对于其中的 阻塞/非阻塞IO 进行了 ...

  5. Netty Reactor 线程模型笔记

    引用: https://www.cnblogs.com/TomSnail/p/6158249.html https://www.cnblogs.com/heavenhome/articles/6554 ...

  6. Reactor 线程模型以及在netty中的应用

    这里我们需要理解的一点是Reactor线程模型是基于同步非阻塞IO实现的.对于异步非阻塞IO的实现是Proactor模型. 一 Reactor 单线程模型 Reactor单线程模型就是指所有的IO操作 ...

  7. Linux 网络编程的5种IO模型:异步IO模型

    Linux 网络编程的5种IO模型:异步IO模型 资料已经整理好,但是还有未竟之业:复习多路复用epoll 阅读例程, 异步IO 函数实现 背景 上一讲< Linux 网络编程的5种IO模型:信 ...

  8. 深入Netty逻辑架构,从Reactor线程模型开始

    本文是Netty系列第6篇 上一篇文章我们从一个Netty的使用Demo,了解了用Netty构建一个Server服务端应用的基本方式.并且从这个Demo出发,简述了Netty的逻辑架构,并对Chann ...

  9. Linux 网络编程的5种IO模型:信号驱动IO模型

    Linux 网络编程的5种IO模型:信号驱动IO模型 背景 上一讲 Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 我们讲解了多路复用等方面的知识,以及有关例程. ...

随机推荐

  1. vue高级

    1.nrm nrm提供了一些最常用的npm包镜像地址,可以快速切换服务器地址下载资源.它只是提供了地址,并不是装包工具.如果没有安装npm,需要安装node,然后直接安装即可.node下载链接:htt ...

  2. MySQL全面瓦解23:MySQL索引实现和使用

    MySQL索引实现 上一篇我们详细了解了B+树的实现原理(传送门).我们知道,MySQL内部索引是由不同的引擎实现的,主要包含InnoDB和MyISAM这两种,并且这两种引擎中的索引都是使用b+树的结 ...

  3. Docker备份迁移

    目录 Docker备份迁移 1.容器保存为镜像 2.镜像打包成压缩文件 3.把压缩文件恢复成镜像 Docker备份迁移 1.容器保存为镜像 将已经装好各种软件的容器再次打包为镜像,这样下次直接装这个镜 ...

  4. Virtual DOM 简直就是挥霍

    彻底澄清"Virtual DOM 飞快"的神话. 注意:原文发表于2018-12-27,随着框架不断演进,部分内容可能已不适用. 近年来,如果你有使用过 JavaScript 框架 ...

  5. 后端程序员之路 21、一个cgi的c++封装

    在"3.fastcgi.fastcgi++"中,我们了解了cgi,也尝试了fastcgi++,这里,再记录一种对fastcgi的封装. 1.cgi接口层    request_t ...

  6. 关于使用C3P0程序报错Having failed to acquire a resource, com.mchange.v2.resourcepool的问题

    由于是新手的问题,C3P0的使用时严格跟着视频来的,但是问题却来的很突然 在导入了三个包以及创建了路径以后 进行测试 class JdbcutilsTest { @Test void TestGetC ...

  7. CCF(消息传递口:80分):字符串相关+队列

    消息传递口 201903-4 本题主要是利用队列进行模拟,因为一开始我没有注意到要按照顺序,所以一开始的解法错误. #include<iostream> #include<algor ...

  8. javaweb遇到的报错及解决方式

    javaweb报错问题以及解决方案 问题(报错信息):Application Server was not connected before run configuration stop, reaso ...

  9. Java 面向对象 04

    面向对象·四级 多态的概述及其代码实现 * A:多态(polymorphic)概述 * 事物存在的多种形态 * B:多态前提 * a:要有继承关系 * b:要有方法重写 * c: 要有父类引用指向子类 ...

  10. AOP(面向切面编程)大概了解一下

    前言 上一篇在聊MemoryCache的时候,用到了Autofac提供的拦截器进行面向切面编程,很明显能体会到其优势,既然涉及到了,那就趁热打铁,一起来探探面向切面编程. 正文 1. 概述 在软件业, ...