1、我眼中的Netty—初识

本文深入探讨了Netty的核心概念,从BIO(同步阻塞I/O)开始,解释了同步与异步、阻塞与非阻塞的区别,并给出BIO的简单示例。接着,介绍了NIO(非阻塞I/O)的优势,如Selector、Buffer和Channel的使用,以及NIO在解决BIO问题上的改进。最后,讨论了Netty基于NIO设计的多Reactor多线程模型,阐述了其在服务端的BossGroup和WorkerGroup的角色,以及如何通过主从Reactor模式提高性能和可扩展性。

“我眼中的Netty”这一系列的文章,着重通过自己的语言来描述自己对Netty核心概念的认识。这其中不对概念做一板一眼的定义与记录。其中在”初始“的章节中,主要介绍Netty的原由,以及Netty的设计模型。

一、BIO网络编程

1、同步&异步与阻塞&非阻塞的区别联系

这两组概念个人理解为是一个是道的层面一个是术的层面,同步&异步为道,阻塞&非阻塞为术。道的层面可以理解一个调用在没有得到结果前不会返回这就是同步,如果是异步的话就是调用会立即返回继续向下执行,被调用这通过状态的方式来通知调用者,这个执行是否结束,Node编程就是异步编程。术的层面关注的是当前执行这个线程在等待调用结果时候的状态,如果在等待结果的时候该调用不会阻塞当前线程那么就是非阻塞,如果这个调用阻塞了当前这个线程就说为是阻塞。如果想要详细的了解这两者的关系应该梳理清楚Node的异步原理以及Java的并发编程,这样会对此有一个更深度的了解。

2、BIO是这样的

通过端口地址创建一个ServerSocket,在一个死循环中不断的执行这个ServerSocket的accept方法来检查是否有客户端连接,如果有连接则会返回一个socket如果没有的话主线程将会一直的阻塞在这里,当拿到这个socket之后主线程调用一个初始好的线程池将这个socket交给一个线程去处理,因为这个处理过程不再主线程中而是在另外一个线程中的所以主线程不会被阻塞,循环将会继续在此到accept这里监听是否有新的客户端请求连接,开始下一次的循环。
在这里插入图片描述

3、BIO的Demo示例

public class BIOServer {
    public static void main(String[] args) throws Exception {
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        //创建ServerSocket
        ServerSocket serverSocket = new ServerSocket(6666);
        while (true) {
            System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());
            //监听,等待客户端连接
            System.out.println("等待连接....");
            final Socket socket = serverSocket.accept();
            System.out.println("连接到一个客户端");
            //就创建一个线程,与之通讯(单独写一个方法)
            newCachedThreadPool.execute(new Runnable() {
                public void run() { //我们重写
                    //可以和客户端通讯
                    handler(socket);
                }
            });
        }
    }
    //编写一个handler方法,和客户端通讯
    public static void handler(Socket socket) {
        try {
            System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());
            byte[] bytes = new byte[1024];
            //通过socket 获取输入流
            InputStream inputStream = socket.getInputStream();
            //循环的读取客户端发送的数据
            while (true) {
                System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());
                System.out.println("read....");
               int read =  inputStream.read(bytes);
               if(read != -1) {
                   System.out.println(new String(bytes, 0, read
                   )); //输出客户端发送的数据
               } else {
                   break;
               }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            System.out.println("关闭和client的连接");
            try {
                socket.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

二、NIO非阻塞编程

1、NIO是这样的

BIO的弊端在与每一个Socket都需要一个线程去处理,当这个Socket发生读写IO操作的时候,线程发生阻塞造成系统资源的浪费,这也浪费也包括线程的上下文切换。NIO做的就是解决这个痛点他抽象出来Selector、Buffer、Channel,Selector负责监听是否有其他的客户端来请求连接,他也可以监听是否有读写事件发生,如果有的话通知相应的线程进行处理。那么既然不是立即处理的那就需要一个缓冲数据的地方起这个作用的就是Buffer,而连接客户端与服务端的通道就是Channel。这三大组件也是Netty框架实现的基础。

2、NIO的设计模型

在这里插入图片描述

3、NIO的Demo示例

  • 使用Buffer进行文件拷贝
    public class NIOFileChannel03 {
        public static void main(String[] args) throws Exception {
            int flag = 0;
            FileInputStream fileInputStream = new FileInputStream("1.txt");
            FileChannel fileChannel01 = fileInputStream.getChannel();
            FileOutputStream fileOutputStream = new FileOutputStream("2.txt");
            FileChannel fileChannel02 = fileOutputStream.getChannel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            while (true) {
                byteBuffer.clear(); //清空buffer
                int read = fileChannel01.read(byteBuffer);
                System.out.println("read =" + read);
                if(read == -1) { //表示读完
                    break;
                }
                //将buffer 中的数据写入到 fileChannel02 -- 2.txt
                byteBuffer.flip();
                fileChannel02.write(byteBuffer);
            }
            //关闭相关的流
            fileInputStream.close();
            fileOutputStream.close();
        }
    }
    
  • Selector的使用
    public class NIOServer {
        public static void main(String[] args) throws Exception{
            //创建ServerSocketChannel -> ServerSocket
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //得到一个Selecor对象
            Selector selector = Selector.open();
            //绑定一个端口6666, 在服务器端监听
            serverSocketChannel.socket().bind(new InetSocketAddress(6666));
            //设置为非阻塞
            serverSocketChannel.configureBlocking(false);
            //把 serverSocketChannel 注册到  selector 关心 事件为 OP_ACCEPT
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("注册后的selectionkey 数量=" + selector.keys().size()); // 1
            //循环等待客户端连接
            while (true) {
                //这里我们等待1秒,如果没有事件发生, 返回
                if(selector.select(1000) == 0) { //没有事件发生
                    System.out.println("服务器等待了1秒,无连接");
                    continue;
                }
                //如果返回的>0, 就获取到相关的 selectionKey集合
                //1.如果返回的>0, 表示已经获取到关注的事件
                //2. selector.selectedKeys() 返回关注事件的集合
                //   通过 selectionKeys 反向获取通道
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                System.out.println("selectionKeys 数量 = " + selectionKeys.size());
                //遍历 Set<SelectionKey>, 使用迭代器遍历
                Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
                while (keyIterator.hasNext()) {
                    //获取到SelectionKey
                    SelectionKey key = keyIterator.next();
                    //根据key 对应的通道发生的事件做相应处理
                    if(key.isAcceptable()) { //如果是 OP_ACCEPT, 有新的客户端连接
                        //该该客户端生成一个 SocketChannel
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        System.out.println("客户端连接成功 生成了一个 socketChannel " + socketChannel.hashCode());
                        //将  SocketChannel 设置为非阻塞
                        socketChannel.configureBlocking(false);
                        //将socketChannel 注册到selector, 关注事件为 OP_READ, 同时给socketChannel
                        //关联一个Buffer
                        socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                        System.out.println("客户端连接后 ,注册的selectionkey 数量=" + selector.keys().size()); //2,3,4..
                    }
                    if(key.isReadable()) {  //发生 OP_READ
                        //通过key 反向获取到对应channel
                        SocketChannel channel = (SocketChannel)key.channel();
                        //获取到该channel关联的buffer
                        ByteBuffer buffer = (ByteBuffer)key.attachment();
                        channel.read(buffer);
                        System.out.println("form 客户端 " + new String(buffer.array()).toString());
                    }
                    //手动从集合中移动当前的selectionKey, 防止重复操作
                    keyIterator.remove();
                }
            }
        }
    }
    

三、Netty设计模式

1、Netty概述

Netty作为一个网络层框架在诸多的微服务中间件中都有广泛的应用,他是基于Java的NIO之上构建,但是由于原生的NIO 操作复杂需要考虑诸多的因素,所以就诞生了设计良好易于使用、高性能、高健壮、安全的Netty。
在这里插入图片描述

2、Netty的设计模型

Netty是基于JavaNIO的,上面的我们知道NIO包含三大组件Selector、Channel、Buffer三大组件,基于这三大组件常见的三种模型可以有单Reactor单线程、单Reactor多线程、多Reactor多线程,三大种类。其中Netty的设计模式是在多Reactor多线程的基础上发展而来的。

  • 单Reactor单线程:这样的模式可以一个线程监听多个Socket,实现多路复用。但是如果一个IO的操作非常耗时,那么他所关联的其他Socket就也会就如阻塞因为当前线程正在处理其他Socket的操作。如果一个Socket发生了阻塞那么并不一定会导致整个线程都阻塞,他可以运行其他的Socket。
  • 单Reactor多线程:上面的模式是对Socket动作的监听以及IO操作的处理都在一个新线程中,此模型中Reactor的线程负责事件的监听以及内容的分发,对于具体的业务逻辑处理则由Worker线程池进行。这样相对于上面的单Reactor单线程就提供了更多的性能起码不会以因为IO事件的处理而对事件的监听造成影响
  • 主从Reactor多线程:这三种的模型的关系就像是层层递进,此模型相对于单Reactor的来说,他将Reactor负责的工作进一步拆分,主Reactor负责对对连接事件的监听,从Reactor负责对读写事件进行监听。正如那句”没有什么是加一层解决不了的“所说,复杂的可伸缩的Netty就是在一层加一层的基础上诞生。这里面说的主Reactor可以对标Netty服务端的BossGroup(他也是可以包含多个EventLoop的,但是❓如何实现多个Selector对一个Channel监听,这就是一个问题了),而从Reactor可以对比与WorkGroup。客户端我们知道只有一个Group,并且他也是只包含一个EventLoop,这是因为他只需要对一个Channel进行监听此处应该可以类比为单Reactor单线程,因为EventLoop在Netty的设计中是从始至终绑定一个线程的,所以说监听与IO的业务逻辑处理都是在一个线程中的。
    在这里插入图片描述

服务端创建BossGroup的时候虽然我们可以通过入参指定此Group可以有多个EventLoop但是在通过bind进行端口绑定的时候应该还是通过一定的算法选取了其中的一个,所以说BossGroup在创建的时候还是直接指定他的EventLoop为1即可,否则就算创建了多个最后选的也就只有一个,下面我们来说一下这个创建的这一块源码的流程:

1、在通过group(bossGroup, workerGroup)来为引导绑定Group的时候是将这两个Group分别绑定在了相关的属性上,其中bossGroup的入参是绑定到了AbstractBootstrap的group上了,对group中EventLoop的绑定是发生在bind的这一步骤的

2、调用bind的时候会通过层层调用在AbstractBootstrap的initAndRegister,首先是通过channelFactory.newChannel();获得一个Channel,之后调用config().group().register(channel);的方法获取到当前Bootstrap中绑定的Group获取到,我要注册的EventLoop

3、由于Group中可能是有多个EventLoop的所以获取是需要一定的算法的,这是通过调用DefaultEventExecutorChooserFactory.PowerOfTwoEventExecutorChooser.next;这里采用到了的是元子类以及与操作防止越界异常

@Override
public EventExecutor next() {
   return executors[idx.getAndIncrement() & executors.length - 1];
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EatsWsTR-1651676111535)(F:\typroa\aimages\image-20220419110558917.png)]

4、通过上面的步骤获取到一个EventLoop,这个EventLoop相对是Nio 中的一个EventLoop,相关的源码在另一个文章中再做详细的介绍。

内容概要:本文档是一份关于“超声谐波成像中幅超声谐波成像中幅度调制聚焦超声引起的全场位移和应变的分析模型(Matlab代码实现)度调制聚焦超声引起的全场位移和应变的分析模型”的Matlab代码实现研究资料,重点构建了一个用于分析在超声谐波成像过程中,由幅度调制聚焦超声所引发的生物组织全场位移应变的数学模型。该模型通过Matlab仿真手段实现了对声场激励下组织力学响应的精确计算可视化,有助于深入理解超声激励组织变形之间的物理机制,提升超声弹性成像的精度可靠性。文档还附带多个相关科研领域的Matlab/Simulink代码实例,涵盖无人机控制、路径规划、电力系统仿真、信号处理、机器学习等多个方向,展示了强大的技术支撑应用拓展能力。; 适合人群:具备Matlab编程基础,从事医学超声成像、生物力学建模、信号图像处理等相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①用于超声弹性成像中组织力学响应的仿真分析;②为开发新型超声诊断技术提供理论模型算法支持;③作为多物理场耦合仿真的教学研究案例,促进跨学科技术融合。; 阅读建议:建议读者结合Matlab代码逐行理解模型实现细节,重点关注声场建模、组织力学方程求解及位移应变后处理部分。同时可参考文档中提供的其他仿真案例,拓宽研究思路,提升综合科研能力。
标题基于SpringBoot的高校餐饮档口管理系统设计实现AI更换标题第1章引言介绍高校餐饮档口管理系统的研究背景、意义、国内外现状及论文方法创新点。1.1研究背景意义阐述高校餐饮档口管理现状及系统开发的重要性。1.2国内外研究现状分析国内外高校餐饮管理系统的研究应用进展。1.3研究方法及创新点概述本文采用的研究方法及系统设计的创新之处。第2章相关理论总结高校餐饮档口管理系统相关的现有理论。2.1SpringBoot框架理论阐述SpringBoot框架的原理、优势及其在Web开发中的应用。2.2数据库设计理论介绍数据库设计的基本原则、方法和步骤。2.3系统安全理论讨论系统安全设计的重要性及常见安全措施。第3章系统需求分析对高校餐饮档口管理系统的功能需求、性能需求等进行详细分析。3.1功能需求分析列举系统需实现的主要功能,如档口管理、订单处理等。3.2性能需求分析分析系统对响应时间、并发处理能力等性能指标的要求。3.3非功能需求分析阐述系统对易用性、可维护性等非功能方面的需求。第4章系统设计详细描述高校餐饮档口管理系统的设计过程。4.1系统架构设计给出系统的整体架构,包括前端、后端和数据库的设计。4.2模块设计详细介绍各个功能模块的设计,如用户管理、档口信息管理等。4.3数据库设计阐述数据库表结构的设计、数据关系及索引优化等。第5章系统实现测试介绍高校餐饮档口管理系统的实现过程及测试方法。5.1系统实现系统各模块的具体实现过程,包括代码编写和调试。5.2系统测试方法介绍系统测试的方法、测试用例设计及测试环境搭建。5.3系统测试结果分析从功能、性能等方面对系统测试结果进行详细分析。第6章结论展望总结本文的研究成果,并展望未来的研究方向。6.1研究结论概括高校餐饮档口管理系统的设计实现成果。6.2展望指出系统存在的不足及未来改进和扩展的方向。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值