BIO、NIO、Netty演化总结

news2024/11/22 10:14:41

关于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 设置处理器
                        @Override
                        protected 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() {
                @Override
                public 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.coloradmin.cn/o/1446057.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java编程练习之类的继承

1.创建银行卡类&#xff0c;并分别设计两个储蓄卡和信用卡子类。 import javax.swing.plaf.BorderUIResource;import java.util.Scanner;class Card {int Id; //银行卡&#xff1b;int password; //密码&#xff1b;double balance2000; //账户存款金额&#xff1b;String A…

论文介绍 FreeControl: 无需额外训练实现文本到图像的空间操控!

论文介绍 FreeControl: 无需额外训练实现文本到图像的空间操控&#xff01; 论文介绍 FreeControl: Training-Free Spatial Control of Any Text-to-Image Diffusion Model with Any Condition 关注微信公众号: DeepGo 项目地址&#xff1a;https://genforce.github.io/freeco…

进程间通信[二]

命名管道 mkfifo filename 创建管道 共享内存 systemV 就是一段内存映射到两个进程之中&#xff0c;这段内存就是双方都可以看到同一块公共空间&#xff0c;变具备了进程间通信的前提条件。把映射去掉&#xff0c;然后释放空间&#xff0c;这就是释放共享内存。 共享内存…

实现安全性

实现安全性 问题陈述 Chris希望阅读位于服务器上的电子邮件消息。他将自己的登录信息发送到服务器已进行验证。因此,Chris决定用基于表单的验证来验证他的登录信息。但是,他首先决定只用基于表单的验证测试登录页面 。 解决方案 要解决上述问题,Chris需要执行以下任务: 用…

Github 2024-02-07 开源项目日报 Top9

根据Github Trendings的统计&#xff0c;今日(2024-02-07统计)共有9个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Rust项目2TypeScript项目2Python项目2Ruby项目1HTML项目1NASL项目1Go项目1C项目1Svelte项目1C项目1 React Nat…

2024年2月5日-2月11日周报

论文阅读 1. 本周计划2. 完成情况2.1 论文摘要2.2 网络结构2.3 损失函数2.4 优化器2.5 代码2.5.1 代码结果2.5.2 代码大致流程 4. 总结及收获4. 下周计划 1. 本周计划 阅读论文《Data-Driven Seismic Waveform Inversion: A Study on the Robustness and Generalization》并实…

【MATLAB】GA_BP神经网络回归预测算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 GA_BP神经网络回归预测算法结合了遗传算法&#xff08;Genetic Algorithm, GA&#xff09;和BP神经网络&#xff08;Backpropagation Neural Network, BPNN&#xff09;&#xff0c;用于解…

Verilog刷题笔记29

题目&#xff1a; Create a 100-bit binary ripple-carry adder by instantiating 100 full adders. The adder adds two 100-bit numbers and a carry-in to produce a 100-bit sum and carry out. To encourage you to actually instantiate full adders, also output the ca…

python+django人力资源管理系统7w5x3

技术栈 后端&#xff1a;python 前端&#xff1a;vue.jselementui 框架&#xff1a;django Python版本&#xff1a;python3.7 数据库&#xff1a;mysql5.7 数据库工具&#xff1a;Navicat 开发软件&#xff1a;PyCharm .设计框架&#xff1a;Vue 1. 表现层&#xff1a;写多…

LocalAI 部署(主要针对 mac m2 启动)

LocalAI 部署 介绍 LocalAI 是免费的开源 OpenAI 替代方案。 LocalAI 充当 REST API 的直接替代品&#xff0c;与本地推理的 OpenAI API 规范兼容。 它无需 GPU&#xff0c;还有多种用途集成&#xff0c;允许您使用消费级硬件在本地或本地运行 LLM、生成图像、音频等等&#…

【北邮鲁鹏老师计算机视觉课程笔记】06 corner 局部特征

【北邮鲁鹏老师计算机视觉课程笔记】06 corner 局部特征 1 局部特征的任务牵引&#xff1a;全景拼接 ①提取特征 ②匹配特征 ③拼接图像 我们希望特征有什么特性&#xff1f; ①可重复性 ②显著性 ③计算效率和表达紧凑性 ④局部性 2 特征点检测的任务 3 角点 在角点&#…

EMC学习笔记(二十五)降低EMI的PCB设计指南(五)

线缆和连接器 1 差模和共模噪声2 串扰3 返回路径数量4 外部PCB -IO 布局建议5 防止噪音和静电放电 tips&#xff1a;资料主要来自网络&#xff0c;仅供学习使用。 设计良好的两层板&#xff0c;和大多数四层板&#xff0c;有最小的辐射。系统级的问题是由于将PCB与任何板外支持…

Linux第52步_移植ST公司的linux内核第4步_关闭内核模块验证和log信息时间戳_编译_并通过tftp下载测试

1、采用程序配置关闭“内核模块验证” 默认配置文件“stm32mp1_atk_defconfig”路径为“arch/arm/configs”; 使用VSCode打开默认配置文件“stm32mp1_atk_defconfg”&#xff0c;然后将下面的4条语句屏蔽掉&#xff0c;如下&#xff1a; CONFIG_MODULE_SIGy CONFIG_MODULE_…

机器学习之局部最优和全局最优

(1)局部最优&#xff0c;就是在函数值空间的一个有限区域内寻找最小值;而全局最优&#xff0c;是在函数值空间整个区域寻找最小值问题。 (2)函数局部最小点是它的函数值小于或等于附近点的点&#xff0c;但是有可能大于较远距离的点。 (3)全局最小点是那种它的函数值小于或等于…

【后端高频面试题--Linux篇】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;后端高频面试题 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 后端高频面试题--Linux篇 Windows和Linux的区别&#xff1f;Unix和Linux有什么区别&#xff1f…

宿舍报修|宿舍报修小程序|基于微信小程序的宿舍报修系统的设计与实现(源码+数据库+文档)

宿舍报修小程序目录 目录 基于微信小程序的宿舍报修系统的设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户小程序功能模块 2、学生信息管理 3、维修人员管理 4、故障上报管理 5、论坛信息管理 四、数据库设计 1、实体ER图 2、具体的表设计如下所示&…

在Visual Studio中搭建Dynamo Python开发环境,效率飞一般的增长

最近在学习Dynamo中Python Script的用法&#xff0c;发现这个东西用起来太不友好了&#xff0c;不支持自动缩进&#xff0c;不支持自动填充和提示。用过Visual Studio做二开的都知道&#xff0c;在引用了Revit api以后&#xff0c;就可以自动填充和提示了。 本来英语就不好&am…

docker本地目录挂载

小命令 1、查看容器详情 docker inspect 容器名称 还是以nginx为例&#xff0c;上篇文章我们制作了nginx静态目录的数据卷&#xff0c;此时查看nginx容器时会展示出来&#xff08;docker inspect nginx 展示信息太多&#xff0c;这里只截图数据卷挂载信息&#xff09;&#…

《二叉树》——4(Leetcode题目练习)

目录 前言&#xff1a; 题目一&#xff1a;《对称二叉树》 思路&#xff1a; 题目二&#xff1a;《单值二叉树》 思路&#xff1a; 题目三&#xff1a;《检查两颗树是否相同》 思路&#xff1a; 题目四&#xff1a;《前序遍历》 思路&#xff1a; 题目五&#xff1a;《…

CTFshow web(php命令执行 55-59)

web55 <?php /* # -*- coding: utf-8 -*- # Author: Lazzaro # Date: 2020-09-05 20:49:30 # Last Modified by: h1xa # Last Modified time: 2020-09-07 20:03:51 # email: h1xactfer.com # link: https://ctfer.com */ // 你们在炫技吗&#xff1f; if(isset($_GET[…