BIO、NIO、Netty演化总结

关于BIO(关于Java NIO的的思考-CSDN博客)和NIO(关于Java NIO的的思考-CSDN博客)在之前的博客里面已经有详细的讲解,这里再总结一下最近学习netty源码的的心得体会

在之前的NIO博客中我们知道接受客户端连接和IO事件的线程是同一个线程,那么就会存在一个问题,就是如果某一个socket连接在读完数据之后,写数据之前,有比较耗时的逻辑,在这个逻辑执行完成之前,都会导致线程无法继续接受客户端请求以及处理其他socket的io事件,那么就会导致整个应用程序性能下降,于是我们可以做进一步的优化,把接受客户端请求和读写时间的处理分开为两组线程来处理,彼此不会相互影响,通常接受客户端的请求时间是一个很快的操作,这样就可以提高应用程序的连接数量,将之前NIO的代码进步一改进如下:

public class AsyncNonBlockingServerWithThreadPool {private Selector selector;private ServerSocketChannel serverChannel;private ByteBuffer buffer;private ExecutorService executorService;public AsyncNonBlockingServerWithThreadPool(int port) throws IOException {// 创建选择器和服务器通道selector = Selector.open();serverChannel = ServerSocketChannel.open();serverChannel.bind(new InetSocketAddress(port));serverChannel.configureBlocking(false);// 注册服务器通道到选择器,并注册接收连接事件serverChannel.register(selector, SelectionKey.OP_ACCEPT);buffer = ByteBuffer.allocate(1024);// 创建线程池,用于处理事件executorService = Executors.newFixedThreadPool(10);}public void start() throws IOException {System.out.println("Server started.");while (true) {// 阻塞等待事件发生selector.select();// 处理事件Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();keyIterator.remove();if (key.isAcceptable()) {// 接收连接事件handleAccept(key);} else if (key.isReadable()) {// 可读事件handleRead(key);}}}}private void handleAccept(SelectionKey key) throws IOException {ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();SocketChannel clientChannel = serverChannel.accept();clientChannel.configureBlocking(false);clientChannel.register(selector, SelectionKey.OP_READ);System.out.println("New client connected: " + clientChannel.getRemoteAddress());}private void handleRead(SelectionKey key) throws IOException {// 将事件处理的代码提交到线程池中executorService.submit(() -> {SocketChannel clientChannel = (SocketChannel) key.channel();buffer.clear();int bytesRead = 0;try {bytesRead = clientChannel.read(buffer);} catch (IOException e) {e.printStackTrace();}if (bytesRead == -1) {// 客户端关闭连接key.cancel();try {clientChannel.close();} catch (IOException e) {e.printStackTrace();}try {System.out.println("Client disconnected: " + clientChannel.getRemoteAddress());} catch (IOException e) {e.printStackTrace();}return;}buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);System.out.println("Received message from client: " + new String(data));});}public static void main(String[] args) {try {AsyncNonBlockingServerWithThreadPool server = new AsyncNonBlockingServerWithThreadPool(8080);server.start();} catch (IOException e) {e.printStackTrace();}}
}

这里可以看到在读取完数据之后,将数据交给另外一个线程池去执行其他的业务逻辑,这样就不会影响主线程接受客户端的请求了,这个代码也是一个最简单的Reactor模式的实现,甚至可以说是一个简单的Netty实现(雏形),也就是下面这张图:mainreactor负责接收客户端连接,然后将连接交给subreactor作进一步的数据读写操作

 这个模型也是实现netty的基石,只是netty在次基础上做了很多进一步的优化,我们来看看一个简单的netty实现的程序:

public class NettyServer {public static void main(String[] args) throws InterruptedException {// 创建 BossGroup 和 WorkerGroup// 说明// 1.创建两个线程组 BossGroup 和 WorkerGroup// 2. BossGroup 只是处理连接请求,真正的和客户端业务处理,会交给 WorkerGroup 完成// 3. 两个都是无线循环EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {// 创建服务器端的启动对象,配置启动参数ServerBootstrap bootstrap = new ServerBootstrap();// 使用链式编程进行设置bootstrap.group(bossGroup, workerGroup) // 设置两个线程组.channel(NioServerSocketChannel.class) // 使用 ioServerSocketChannel 作为服务器通道实现.option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列等待连接个数.childOption(ChannelOption.SO_KEEPALIVE, true)  // 设置保持活动连接状态.childHandler(new ChannelInitializer<SocketChannel>() {// 创建一个通道初始化对象(匿名对象)// 给 pipeline 设置处理器@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new NettyServerHandler());}});  // 给我们的WorkerGroup 的 EventLoop 对应的管道设置处理器System.out.println(".....服务器 is ready.....");// 绑定一个端口,并且同步,生成一个ChannelFuture对象// 启动服务器ChannelFuture cf = bootstrap.bind(6668).sync();
// 给 cf 注册监听器,监控我们关心的事件cf.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (cf.isSuccess()) {System.out.println("监听端口 6668 成功");} else {System.out.println("监听端口 6668 失败");}}});// 对关闭通道进行监听cf.channel().closeFuture().sync();}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}

其中bossGroup就是mainreactor,也就是负责接收客户端的连接的,workerGroup就是subreactor,也就是专门负责读写数据的。

netty的源码很多很长,但是我们要有一个清晰的认知:

1、netty是基于Jdk原生的NIO来实现的(封装了原生NIO),并不是重新实现了一套IO框架

2、NIO网络编程关键的三个组件:

      多路复用器(selector):负责挑选出就绪的事件对应的channe

      通道(channel):包括服务端的Serversocketchannel和客户端的socketchannel

      缓冲区(bytebuff):读写数据,socket数据的都是通过channe向缓冲区读写

netty相比于原生NIO的优势:

1、采用reactor模型,所有的操作均采用事件通知机制来实现(包括连接和读写数据,基于jdk的future之上做了封装,运用观察者模式和装饰器模式,避免future.get带来的程序阻塞)

2、缓冲区的操作不需要手动反转,并且在申请内存时会根据上一次申请的内存大小动态调整

3、在从线程池中挑选线程处理任务时的算法优化

4、一个EventLoop(可以理解为其实就是一个线程),可以绑定多个socketchannel,也就是一个线程可以同时处理多个连接的数据的读写

5、一个socketchannel只会与一个EventLoop绑定,一个EventLoop独立拥有一个selector,这样就避免了多线程之间的竞争

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/465926.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL篇----第二十二篇

系列文章目录 文章目录 系列文章目录前言一、什么是表级锁二、什么是页级锁三、什么是行级锁四、什么是悲观锁前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 一、…

【ES】--ES集成热更新自定义词库(字典)

目录 一、问题描述二、具体实施1、Tomcat实现远程扩展字典2、验证生效3、ES配置远程扩展字典4、为何不重启ES能实现热更新 一、问题描述 问题现象: 前面完成了自定义分词器词库集成到ES中。在实际项目中词库是时刻在变更的&#xff0c;但又不希望重启ES&#xff0c;对此我们应…

C++:面向对象——类的构造

1.1学会面向对象的编程思想 面向对象的英文缩写是OO&#xff0c;它是一种设计思想。 面向对象有3大特点&#xff1a;封装、继承和多态。 1.封装 封装有两个作用&#xff0c;一个是将不同的小对象封装成一个大对象&#xff1b;另外一个是把一部分内部属性和功能对外界屏蔽。…

mac无法往硬盘里存东西 Mac硬盘读不出来怎么办 Mac硬盘格式 硬盘检测工具

mac有时候会出现一些问题&#xff0c;比如无法往硬盘里存东西&#xff0c;或者无法往硬盘上拷贝文件。这些问题会给用户带来很大的困扰&#xff0c;影响正常的工作和学习。那么&#xff0c;mac无法往硬盘里存东西&#xff0c;mac无法往硬盘上拷贝怎么办呢&#xff1f;软妹子将为…

关于node与node-sass那些事

昨晚找了之前的一个项目想要复习下&#xff0c;结果npm i报错&#xff0c;大致意思就是noda-sass的版本和node的对不上&#xff0c;那怎么办呢&#xff1a; 1.换node版本&#xff0c;那好吧&#xff0c;首先要明白&#xff0c;对应的版本关系 2.然后我开始用nvm换node版本&am…

【深度学习 目标检测】R-CNN系列算法全面概述(一文搞懂R-CNN、Fast R-CNN、Faster R-CNN的来龙去脉)

&#x1f680;个人主页&#xff1a;为梦而生~ 关注我一起学习吧&#xff01; &#x1f4a1;相关专栏&#xff1a; 深度学习 &#xff1a;现代人工智能的主流技术介绍 机器学习 &#xff1a;相对完整的机器学习基础教学&#xff01; &#x1f4a1;往期推荐&#xff1a; 【机器学…

Matplotlib初探:认识数据可视化与Matplotlib

Matplotlib初探&#xff1a;认识数据可视化与Matplotlib Fig.1 利用Matplotlib进行数据可视化( 可视化代码见文末) &#x1f335;文章目录&#x1f335; &#x1f333;引言&#x1f333;&#x1f333;一、数据可视化简介&#x1f333;&#x1f333;二、Matplotlib库简介&#x…

MySQL 升级脚本制作

当数据库更新字段后或添加一些基础信息&#xff0c;要对生产环境进行升级&#xff0c;之前都是手动编写sql&#xff0c;容易出错还容易缺失。 通过 Navcat 工具的数据库结构同步功能和数据同步功能完成数据库脚本的制作。 一、结构同步功能 1、选择 工具–结构同步&#xff1…

Qt Qt Creator安装与工程介绍

一.Qt概述 什么是Qt&#xff1a;Qt是一个跨平台的C图形用户界面应用程序框架。它为应用程序开发者提供建立图形界面所需的所有功能。它是完全面向对象的&#xff0c;很容易扩展&#xff0c;并且允许真正的组件编程。 二.Qt Creator下载安装 下载地址&#xff1a;Index of /a…

1【算法】——最大子数组问题(maximum subarray)

一.问题描述 假如我们有一个数组&#xff0c;数组中的元素有正数和负数&#xff0c;如何在数组中找到一段连续的子数组&#xff0c;使得子数组各个元素之和最大。 二.问题分析 分治法求解&#xff1a; 初始状态&#xff1a; low0&#xff1b;highA.length-1&#xff1b;mid&am…

板块一 Servlet编程:第二节 Servlet的实现与生命周期 来自【汤米尼克的JAVAEE全套教程专栏】

板块一 Servlet编程&#xff1a;第二节 Servlet的实现与生命周期 一、Servlet相关概念Serlvet的本质 二、中Web项目中实现Servlet规范&#xff08;1&#xff09;在普通的Java类中继承HttpServlet类&#xff08;2&#xff09;重写service方法编辑项目对外访问路径 二、Servlet工…

读书笔记之《重塑大脑重塑人生》:大脑强大的可塑性

《重塑大脑重塑人生》作者是诺曼道伊奇&#xff0c;原作名: The Brain That Changes Itself: Stories of Personal Triumph from the Frontiers of Brain Science &#xff0c;于 2015-1-20出版。 诺曼•道伊奇&#xff08;Norman Doidge&#xff09;是医学博士&#xff0c;精…