1、NIO实现Client-Server通讯

我们将NIO实现Client-Server通讯的流程分为4步,见下面伪代码:

class NioServer{    main(){初始化 nio 相关组件for(循环处理监听端口的事件){1、与客户端建立连接;2、读取客户端发送的数据;3、处理数据;4、发送响应给客户端}}
}

流程示意图如下

图 1-1

我们写代码实现一下

public class NioServer {public static void main(String[] args) throws IOException {// 初始化 nio 相关组件Selector sel = Selector.open();ServerSocketChannel ssc = ServerSocketChannel.open();ssc.configureBlocking(false);// 开始监听9000端口ssc.bind(new InetSocketAddress(9000));        // 循环处理9000端口的事件while (!Thread.interrupted()) {// 将当前所有的事件封装为SelectionKey放入Set中sel.select();Set keySet = sel.selectedKeys();Iterator it = keySet.iterator();// 遍历Set中的事件while (it.hasNext()) {SelectionKey key = (SelectionKey) (it.next());if (key.isAcceptable()) {// 1、accept建立连接SocketChannel channel = ssc.accept();channel.register(sel, SelectionKey.OP_READ);} else if (key.isReadable()) {SocketChannel channel = (SocketChannel) key.channel();// 2、读取客户端发送的数据String readedStr = SocketReader.read(channel);// 3、处理数据String processedStr = process(readedStr);// 4、发送响应给客户端channel.write(ByteBuffer.wrap(processedStr.getBytes()));}it.remove();}}}public static String process(String str) {try {Thread.sleep(2 * 1000);} catch (InterruptedException e) {e.printStackTrace();}return "processed string:" + str;}
}

一步一图分析处理流程

初始化nio服务端

创建一个Selector,Selector下有个Set

创建一个ServerSocketChannel,并将其注册到Selector


注册结果返回一个SelectioinKey,我们用下图表示

当一个客户端client-1发送连接请求时,ServerSocketChannel会监听到这个请求事件,select方法会将该事件对应SelectionKey放入Set中,调用ServerSocketChannel的accept方法创建一个SocketChannel与客户端建立连接

1、与客户端建立连接

假设有个client-1创建一个SocketChannel发送一个请求过来,服务端创建一个SocketChannel与之建立连接,可以把这两个SocketChannel看做网络连接两头的端点

client-2、…client-n 发送连接请求也是如此

2、读取客户端发送的数据

客户端与服务端建立连接后,client就可以向

3、处理数据

4、发送响应给客户端

1 ~ 4 步是串行阻塞执行的,无法充分利用现代多核CPU的性能,这个实现性很低!

如何改进呢? 使用多线程使得1 ~ 4步并行执行。

我们一步步来重构这个流程。

业务数据处理的时间一般都比较长,所以我们首先使用单独的线程来执行第3步 ,因为第4步依赖第3步,所以第4步也放入线程中,示例代码如下:

class Server{main(){for(循环处理监听端口的事件){1、accept建立连接;2、读取客户端发送的数据;new Thread(new Runnable(){3、处理数据;4、发送响应给客户端}).start();}}
}

这里


Reactor 反应器模式,角色有

1、Reactor

负责派发IO事件给对应的处理器处理。

连接请求发送给连接处理器-Acceptor,

2、 Acceptor

负责接受client的连线请求,然后给client绑定一个Handler并注册IO事件到Reactor上监听。

3、Handler
负责处理与client交互的事件或行为。

通常因为Handler要处理与所对应client交互的多个事件或行为,可以状态模式来实现Handler。

Reactor 将不同的事件转发到不同的 “事件处理器” 进行处理

1、Reactor 流程

我们以单个客户端连接为例,介绍一下整个流程。

1.1、初始化NIO网络的设置

大家需要明白 SelectionKey 中有 Selector和Channel(ServerSocketChannel 或 SocketChannel)的引用,见下面第一张图 (第二张关系图有兴趣可以看看):

可以通过下面两个方法获取 SelectorChannel

 SelectableChannel channel = SelectionKey.channel();Selector selector = SelectionKey.selector();

|SelectionKey 简略图|

三者关系图
1.2、启动服务端

启动服务端就是使用ServerSocketChannel 监听端口,循环执行 Selector.select() 这个阻塞方法来获取 SelectionKey集合,即在有事件发生时,JDK 底层会基于OS,将所有事件封装为Set集合 Set<SelectionKey> 作为 select 方法的返回值

1.3、处理客户端连接请求

服务启动后,一个客户端发送连接请求过来,select 方法退出阻塞,返回 Set<SelectionKey>

图1 - 启动服务端

下面我们来完成服务端的部分功能:网络服务初始化工作,代码如下:

伪代码:

class STReactor {STReactor (){1、监听指定的端口;2、向Selector注册关注的 “连接请求事件”;}run(){while(true){if(selector.select()>0){3、获取请求事件;//4、TODO 处理请求}          }}
}

具体实现代码:

public class STReactor implements Runnable {private final ServerSocketChannel ssc;private final Selector selector;public STReactor(int port) throws IOException {// 1、监听指定的端口selector = Selector.open();ssc = ServerSocketChannel.open();InetSocketAddress addr = new InetSocketAddress(port);ssc.bind(addr);ssc.configureBlocking(false);// 2、向Selector注册关注的 “连接请求事件”SelectionKey sk = ssc.register(selector, SelectionKey.OP_ACCEPT);}@Overridepublic void run() {while (!Thread.interrupted()) {try {if (selector.select() == 0){continue;}                    } catch (IOException e) {e.printStackTrace();}Set<SelectionKey> selectedKeys = selector.selectedKeys(); //已就绪事件的key集合Iterator<SelectionKey> it = selectedKeys.iterator();while (it.hasNext()) {// 3、获取请求事件SelectionKey sk = (it.next());// 4、TODO 处理请求it.remove();}}}
}

我们使用 AcceptHandler 来处理连接请求,最终建立连接,所谓的连接,从JDK的角度来看就是 SocketChannel ,且以 SelectionKey的形式存在于 Selector

1.4、处理客户端消息

建立好连接后,客户端就可以发送消息了,服务端我们使用 RWHandler 处理器来处理客户端发送过来的消息

不同的事件需要不同的 “事件处理器” 来处理,此时我们就需要一个 “路由转发机制”SelectionKeyattach()方法了解一下)

+++++++++++++++++++++++++++++++++++++++++++++++++

+++++++++++++++++++++++++++++++++++++++++++++++++

参考资料

网络编程基础(3) : IO多路复用(单线程)
网络编程基础(4) : IO多路复用(多线程)
网络编程基础(5) : IO多路复用(多Reactor)(主从式Reactor)

一步步Netty的基石 - Reactor模式相关推荐

  1. Netty基础入门——Reactor模式

    文章目录 1. 前言 2. 单线程Reactor模式 3. 多线程Reactor模式 4. Reactor模式和观察者模式 1. 前言 Reactor模式是高性能.高并发技术中非常重要的基础知识,只有 ...

  2. 【Netty】反应器 Reactor 模式 ( 单反应器 Reactor 单线程 | 单反应器 Reactor 多线程 )

    文章目录 一. 反应器 ( Reactor ) 模式 二. 反应器 ( Reactor ) 模式两大组件 三. 单反应器 ( Reactor ) 单线程 四. 单反应器 ( Reactor ) 单线程 ...

  3. 【Netty】Netty 简介 ( 原生 NIO 弊端 | Netty 框架 | Netty 版本 | 线程模型 | 线程 阻塞 IO 模型 | Reactor 模式引入 )

    文章目录 一. NIO 原生 API 弊端 二. Netty 简介 三. Netty 架构 四. Netty 版本 五. Netty 线程模型 六. 阻塞 IO 线程模型 七. 反应器 ( React ...

  4. Netty出现的原因以及多种Reactor模式

    一.原生NIO存在的问题 NIO的类库与API繁杂,需要熟练掌握Selector.ServerSocketChannel.SocketChannel.Bytebuffer等要求熟悉Java多线程编程和 ...

  5. Java进阶知识点5:服务端高并发的基石 - NIO与Reactor模式以及AIO与Proactor模式

    一.背景 要提升服务器的并发处理能力,通常有两大方向的思路. 1.系统架构层面.比如负载均衡.多级缓存.单元化部署等等. 2.单节点优化层面.比如修复代码级别的性能Bug.JVM参数调优.IO优化等等 ...

  6. Netty之reactor模式

    Netty之Reactor模式 目录 1. 单线程模型 2. 多线程模型 3. 主从多线程模型 参考资料 正文 无论是C++还是Java编写的网络框架,大多数都是基于Reactor模式进行设计和开发, ...

  7. Netty快速入门与Reactor模式

    Netty概述 原生NIO存在的问题 NIO的类库和API繁杂,使用麻烦:需要熟练掌握Selector.ServerSocketChannel.SocketChannel.ByteBuffer等 需要 ...

  8. java基础巩固-宇宙第一AiYWM:为了维持生计,Redis基础Part6(Redis的应用场景、Redis是单线程的速度还快、Redis线程模型:Reactor模式、事件、发布订阅、管道)~整起

    PART1-1:为什么Redis是单线程的 Redis单线程是指: Redis的网络IO和键值对读写是由一个线程来完成的.这也是 Redis 对外提供键值存储服务的主要流程.Redis的其他功能,比如 ...

  9. 高性能IO之Reactor模式

    讲到高性能IO绕不开Reactor模式,它是大多数IO相关组件如Netty.Redis在使用的IO模式,为什么需要这种模式,它是如何设计来解决高性能并发的呢? 最最原始的网络编程思路就是服务器用一个w ...

最新文章

  1. Python计算训练数据集(测试集)中某个分类变量阴性(阳性)标签样本的不同水平(level)或者分类值的统计个数以及比例
  2. uboot i2c 命令的读写测试
  3. ReSharper“无法解析符号”,即使在项目构建时
  4. 轻松处理高于平常10倍的视频需求,还能节省60%的IT成本,蓝墨做对了什么?
  5. 【渝粤题库】陕西师范大学100141大学英语(三)作业 (专升本、高起本)
  6. Linux操作系统CentOS7安装
  7. vCard主题个人简历主题
  8. ApacheCN 2019Q1 总结
  9. [转]MSP430学习心得---时钟
  10. libreoffice 开发文档_LibreOffice中文 | linux软件
  11. Linux数据报文的来龙去脉
  12. python中if嵌套语句_选择结构-if..elif语句和if语句的嵌套
  13. 老年人-傻妞机器人安装及使用教程
  14. win7计算机开机启动项设置,如何设置WIN7开机启动项?
  15. js中如何将object转化为json数据,json数据转js对象
  16. vm虚拟mac系统 apple id 无法登陆
  17. 透过率和反射率的关系_玻璃透过率、反射率和吸收率的关系.doc
  18. 南京邮电大学网络信息安全——网络数据包捕获WireShark(实验一)
  19. 基于JAVA房产中介预约看房系统设计与实现 开题报告
  20. html学习笔记-B站动力节点

热门文章

  1. [过年菜谱之]清蒸鲈鱼
  2. ant 时 --java.lang.NoSuchMethodError: org.apache.tools.ant.util.FileUtils.getFileUtils 解决方法
  3. Soul 网关源码阅读(四)Dubbo请求概览
  4. linux 内核 ide,Linux设备驱动程序学习(7)-内核的数据类型
  5. thymeleaf html模块化,SpringBoot中使用Thymeleaf模板开发的后台管理框架
  6. python中numpy函数fft_如何在PyTorch中正确使用Numpy的FFT函数?
  7. 计算机原理 ---- 程序之下
  8. KendoUI--Grid api 出现的问题
  9. 2007级计算机技术专科毕业设计,2007级计算机科学与技术本科毕业设计选题
  10. php zhxing iptables,Linux iptables 扩展 ipset 使用教程