上篇文章中写了一些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. Java基本概念:多态

    一.简介 描述: 多态性是面向对象编程中的一个重要特性,主要是用来实现动态联编的.换句话说,就是程序的最终状态只有在执行过程中才被决定,而非在编译期间就决定了.这对于大型系统来说能提高系统的灵活性和扩 ...

  2. kubernetes cpu限制参数说明

    docker CPU限制参数 Option Description --cpus=<value> Specify how much of the available CPU resourc ...

  3. linux_MYSQL 数据库自动备份并压缩和删除历史备份

    1. 创建shell脚本 #! /bin/bash# MySQL用户user="root"# MySQL密码userPWD="123456789"# 需要定时备 ...

  4. go中sync.Once源码解读

    sync.Once 前言 sync.Once的作用 实现原理 总结 sync.Once 前言 本次的代码是基于go version go1.13.15 darwin/amd64 sync.Once的作 ...

  5. Node.js 模块化你所需要知道的事

    一.前言 我们知道,Node.js是基于CommonJS规范进行模块化管理的,模块化是面对复杂的业务场景不可或缺的工具,或许你经常使用它,但却从没有系统的了解过,所以今天我们来聊一聊Node.js模块 ...

  6. rest framework Genericview

    通用视图 Django的通用视图...被开发为普通使用模式的快捷方式......他们采取某些共同的习惯和模式的发展观和抽象,从而使您可以快速地将数据写入的共同看法,而不必重复自己发现的. - Djan ...

  7. Web全段重点整理

    1.  HTML+CSS 1.1. HTML+CssDay01 1.1.1. 常用普通标签 常用标签如下 div span a p ul+li h1-h6 img 代码示例: <img src= ...

  8. 基础篇:java.security框架之签名、加密、摘要及证书

    前言 和前端进行数据交互时或者和第三方商家对接时,需要对隐私数据进行加密.单向加密,对称加密,非对称加密,其对应的算法也各式各样.java提供了统一的框架来规范(java.security)安全加密这 ...

  9. FIL怎么获得?FIL在哪里购买?

    从一些交易网站可以看到,FIL 这几天有一个比较大的涨幅,这让许多投资 FIL 的朋友大松一口气:FIL,你终于不装睡了.估计许多关注区块链的小伙伴看到消息又要问了:FIL 怎么获得?FIL 在哪里购 ...

  10. 7、Spring教程之使用注解开发

    1.说明 在spring4之后,想要使用注解形式,必须得要引入aop的包 <dependency> <groupId>org.springframework</group ...