Netty源码阅读(2)之——服务端源码梗概

news2025/7/18 8:14:19

上文我们把客户端源码梗概大致了解了一下,这样再了解服务端源码就轻松一点,我们将从服务端和客户端的区别着手去解析。

目录

区别

 ④

 ①


区别

 ④

客户端:.option(ChannelOption.TCP_NODELAY, true)

在TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认。为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据。这里就涉及到一个名为Nagle的算法,该算法的目的就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。
TCP_NODELAY就是用于启用或关于Nagle算法。如果要求高实时性,有数据发送时就马上发送,就将该选项设置为true关闭Nagle算法;如果要减少发送次数减少网络交互,就设置为false等累积一定大小后再发送。默认为false。

服务端:.option(ChannelOption.SO_BACKLOG, 100)

服务器端TCP内核模块维护有2个队列,我们称之为A,B,客户端向服务端connect的时候,第二次握手时TCP内核模块把客户端连接加入到A队列中,然后第三次握手时TCP把客户端连接从A队列移到B队列。连接完成,应用程序的accept会返回,accept从B队列中取出完成三次握手的连接。

A队列和B队列的长度之和是backlog,当A,B队列的长之和大于backlog时,新连接将会被TCP内核拒绝,所以,如果backlog过小,可能会出现accept速度跟不上,A.B 队列满了,导致新客户端无法连接,要注意的是,backlog对程序支持的连接数并无影响,backlog影响的只是还没有被accept 取出的连接

 常用的还有:.option(ChannelOption.SO_REUSEADDR, true)

参数表示允许重复使用本地地址和端口, 比如,某个服务器进程占用了TCP的80端口进行监听,此时再次监听该端口就会返回错误,
使用该参数就可以解决问题,该参数允许共用该端口,这个在服务器程序中比较常使用,

客户端:.channel(NioSocketChannel.class)

服务端:.channel(NioServerSocketChannel.class)

他们的如何加载以及类型确定的过程是相同的,所以我们着重来看下实例化的不同。

和NioSocketChannel相似,调用了newSocket方法,但是接下来调用有所不同。服务端调用的是openServerSocketChannel方法,而客户端调用的是openSocketChannel方法,顾名思义,一个是客户端的 Java SocketChannel, 一个是服务器端的 Java ServerSocketChannel。

 接下来调用重载方法,这里传入的是SelectionKey.OP_ACCEPT,有 Java NIO Socket 相关知识的朋友明白, Java NIO 是一种 Reactor 模式, 我们通过 selector 来实现 I/O 的多路复用复用. 在一开始时, 服务器端需要监听客户端的连接请求, 因此在这里我们设置了 SelectionKey.OP_ACCEPT。我们还有客户端的印象吗?下图做对比

 接着逐步调用构造器,和客户端代码一样的,会实例化unsafe和pipeline

    protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        unsafe = newUnsafe();
        pipeline = newChannelPipeline();
    }

但是newUnsafe又有所不同,服务端 unsafe 字段其实是一个 AbstractNioMessageChannel#AbstractNioUnsafe 的实例。而客户端是AbstractNioByteChannel#NioByteUnsafe的实例

 ①

在客户端的时候, 我们只提供了一个 EventLoopGroup 对象, 而在服务器端的初始化时, 我们设置了两个 EventLoopGroup, 一个是 bossGroup, 另一个是 workerGroup. 那么这两个 EventLoopGroup 都是干什么用的呢? 其实呢, bossGroup 是用于服务端 的 accept 的, 即用于处理客户端的连接请求. 而 workerGroup, 其实就是实际上干活的啦, 它们负责客户端连接通道的 IO 操作。如下图所示

服务器端 bossGroup 不断地监听是否有客户端的连接, 当发现有一个新的客户端连接到来时, bossGroup 就会为此连接初始化各项资源, 然后从 workerGroup 中选出一个 EventLoop 绑定到此客户端连接中. 那么接下来的服务器与客户端的交互过程就全部在此分配的 EventLoop 中了 。来跟进源码分析一下。

    public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        super.group(parentGroup); // 1
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        // 2
        this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
        return this;
    }

上边代码做了两件事。1是进入AbstractBootstrap的group方法指定group属性,在客户端就是直接来到这里。2是指定ServerBootstrap的childGroup属性。初始化完属性后,那么在哪里用到他们呢?

bossGroup:AbstractBootstrap.bind -> AbstractBootstrap.doBind -> AbstractBootstrap.initAndRegister把boosGroup和NioServerSocketChannsl 关联起来了

ChannelFuture regFuture = config().group().register(channel);

 workGroup:仍然是initAndRegister方法里,出现的init(channel)。

   void init(Channel channel) {
        // 只贴出和childGroup有关的关键代码
        final EventLoopGroup currentChildGroup = childGroup;
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) {
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

init 方法在 ServerBootstrap 中重写了,而childGroup主要是封装成为ServerBootstrapAcceptor类,所以我们着重来看这个类。

我们点进去一看,原来是个静态内部类。有关childGroup的操作主要是放在了类重写的channelRead方法里。

 childGroup.register(child).addListener(...);

这里的child是一个NioSocketChannel,意思是workerGroup 中的一个 EventLoop 和 NioSocketChannel 关联了。

那么,这个channelRead方法什么时候被调用呢?当一个 client 连接到 server 时, Java 底层的 NIO ServerSocketChannel 会有一个 SelectionKey.OP_ACCEPT 就绪事件, 接着就会调用到 NioServerSocketChannel.doReadMessages。

            SocketChannel ch = SocketUtils.accept(javaChannel());
            if (ch != null) {
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }

 通过accept方法获取到客户端的连接,然后封装为NioSocketChannel,传入的this就是指NioServerSocketChannel也就是封装NioSocketChannel的父channel。接下来就经由 Netty 的 ChannelPipeline 机制, 将读取事件逐级发送到各个 handler 中, 于是就会触发前面我们提到的 ServerBootstrapAcceptor.channelRead。

和boosGroup、workGroup一样,这里出现了handler、childHandler,那么他会不会和前者两个一样?一个处理连接,一个处理IO事件?我们来看下代码。

在④中我们提到了init 方法在 ServerBootstrap 中的重写。里边也牵扯到了对handler的操作

        final ChannelHandler currentChildHandler = childHandler; // 1

        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler(); //2
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });

首先看2(上边代码注释2),这里的config.handler()就是服务端代码.handler(new LoggingHandler(LogLevel.INFO))指定的handler,如果不为空则添加到pipeline。

 接着来看1(上边代码注释1),这里的childHandler仍然是构建ServerBootstrapAcceptor的属性,点进这个类,发现这个childHandler在ServerBootstrapAcceptor重写的channelRead方法里(还记得在什么时候调用吗?在连接建立起来之后)

child.pipeline().addLast(childHandler);

还记得这个child吗,他就是一个NioSocketChannel,在他的pipeline中添加了childHandler。

小结以下就是

  • handler 是在 accept 阶段起作用, 它处理客户端的连接请求.
  • childHandler 是在客户端连接建立以后起作用, 它负责客户端连接的 IO 交互.

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

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

相关文章

贪心算法小结

A-金银岛 某天KID利用飞行器飞到了一个金银岛上&#xff0c;上面有许多珍贵的金属&#xff0c;KID虽然更喜欢各种宝石的艺术品&#xff0c;可是也不拒绝这样珍贵的金属。但是他只带着一个口袋&#xff0c;口袋至多只能装重量为w的物品。岛上金属有s个种类, 每种金属重量不同&am…

ffmpeg视频编解码 demo初探(一)(包含下载指定windows版本ffmpeg)分离视频文件中的视频流每一帧YUV图片

参考文章1&#xff1a;YUV数据流编码成H264 参考文章2&#xff1a;【FFmpeg编码实战】&#xff08;1&#xff09;将YUV420P图片集编码成H264视频文件 文章目录第一个项目&#xff1a;分离视频文件中的视频流每一张图片弯路步入正轨下载官方编译的ffmpeg4.3&#xff08;win64-g…

SpringFramework:SpringBean的生命周期

SpringFramework&#xff1a;SpringBean的生命周期 文章目录SpringFramework&#xff1a;SpringBean的生命周期一、SpringBean的生命周期1. 实例化 Bean2. 填充属性&#xff08;DI&#xff09;3. 初始化4. 销毁二、BeanDefinition1. 基本概念2. 大致结构3. Spring 构建它的优势…

深度学习必备Python基础知识充电2

一、python中的类 1.1 python中是有内置的数据类型的 intstr 1.2 创建新的数据类型 自定义类来实现这样的功能 二、年轻人的第一个python类 2.1 来尝试一下 # 年轻人的第一个自定义python类class Man:def __init__(self, name):self.name nameprint(initialized Succes…

【优雅的参数验证@Validated】@Validated参数校验的使用及注解详解——你还在用if做条件验证?

Validated参数校验的使用及注解详解你还在用if做条件验证吗&#xff1f;一、优雅的参数验证Validated1.Valid和Validated的用法(区别)2.引入并使用Validated参数验证二、javax.validation.constraints下参数条件注解详解三、自定义条件注解你还在用if做条件验证吗&#xff1f; …

【云原生之K8s】 Pod控制器

文章目录一、Pod控制器及其功用二、控制器的类型1.Deployment2.StatefulSet2.1 StatefulSet的组成2.2 常规service和无头服务区别2.3 示例小结3.DaemonSet4.Job5.CronJob一、Pod控制器及其功用 Pod控制器&#xff0c;又称之为工作负载&#xff08;workload&#xff09;&#x…

【毕业设计】机器视觉火车票识别系统 - python 深度学习

文章目录0 前言1 课题意义1.1 课题难点&#xff1a;2 实现方法2.1 图像预处理2.2 字符分割2.3 字符识别2.3.1 部分实现代码3 实现效果4 最后0 前言 &#x1f525; Hi&#xff0c;大家好&#xff0c;这里是丹成学长的毕设系列文章&#xff01; &#x1f525; 对毕设有任何疑问…

疑似大厂泄露!阿里内部Redis教程笔记,细节点满/效率翻倍

Redis是一个key-value存储系统&#xff0c;是当下互联网公司广泛采用的NoSQL数据库之一&#xff0c;也是Java程序员应知应会的必备技术。 这套笔记教程采用Redis 6.2.1版本&#xff0c;内容由浅入深&#xff0c;循序渐进&#xff0c;从Redis的基本概念开启讲解&#xff0c;内容…

React核心技术浅析

1. JSX与虚拟DOM 我们从React官方文档开头最基本的一段Hello World代码入手: ReactDOM.render(<h1>Hello, world!</h1>,document.getElementById(root) );这段代码的意思是通过 ReactDOM.render() 方法将 h1 包裹的JSX元素渲染到id为“root”的HTML元素上. 除了在…

NVIDIA Grace Hopper架构深度解析

NVIDIA Grace Hopper架构深度解析 NVIDIA Grace Hopper Superchip 架构是第一个真正的异构加速平台&#xff0c;适用于高性能计算 (HPC) 和 AI 工作负载。 它利用 GPU 和 CPU 的优势加速应用程序&#xff0c;同时提供迄今为止最简单、最高效的分布式异构编程模型。 科学家和工程…

Python~Pandas 小白避坑之常用笔记

Python~Pandas 小白避坑之常用笔记 提示&#xff1a;该文章仅适合小白同学&#xff0c;如有错误的地方欢迎大佬在评论处赐教 文章目录Python~Pandas 小白避坑之常用笔记前言一、pandas安装二、数据读取1.读取xlsx文件2.读取csv文件三、重复值、缺失值、异常值处理、按行、按列剔…

pytest allure 生成报告过程

allure 下载地址&#xff1a;Releases allure-framework/allure2 GitHub 下载好后配置环境变量执行&#xff1a; allure --version 看见版本号就算配置成功了 pytest allure 生成报告过程 allure添加测试类名&#xff0c;方法名&#xff0c;步骤&#xff1a; allure.fea…

【附源码】计算机毕业设计JAVA教学成果管理平台录像演示

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven Vue 等等组成&#xff0c;B/…

教你一招轻松搞定mp3格式转换

第一种&#xff1a;ncm转mp3 经常使用网易云音乐的朋友应该会发现&#xff0c;网易云VIP音乐下载后&#xff0c;有些音乐是ncm格式的&#xff0c;无法导入PR或者一些编辑软件。 解决方法如下&#xff1a; 利用在线网站处理——Convertio 第一步&#xff1a;打开谷歌浏览器客户端…

9家美发连锁店老板一天剪辑1000个视频,用呆头鹅批量剪辑软件剪

1.1呆头鹅批量剪辑软件核心优势 01.我们的产品是经过市场考验的&#xff0c;像有结果的人学习&#xff0c;买有结果的产品。 02.3年的打磨&#xff0c;更新和删除了200多个模块 03.100多次持续优化更新 04.10000个公司和工作室和个人的使用和建议。 05.一个用户至少做出100…

磁盘有空间但无法创建文件

面试原题 我们去面试的时候,面试官通常会问一个问题, “小伙子,你在这些年的工作中,遇到过什么棘手的问题没有? 面试官问这个问题,无非想知道以下几件事情 你有没有过处理疑难问题的经验你解决问题的思路和能力如何你是怎么解决的你解决完这个问题有哪些收获 面试错误示范 …

Java生成验证码+动态分析技术+【实训10】HTML信息隐藏(信息安全技术作业)

Java生成验证码 第1关&#xff1a;使用Servlet生成验证码 任务要求参考答案评论 任务描述相关知识 为什么要有验证码&#xff0c;什么是验证码如何使用Servlet生成验证码编程要求测试说明任务描述 本关任务&#xff1a;使用servlet生成验证码。 相关知识 验证码在我们登陆…

硬链接及软连接引出的inode

inode定义 inode是linux系统中用作数据索引的标识符。简单来说&#xff0c;inode指示了一个文件的基本信息&#xff0c;如inode编号、修改时间、文件的位置等。 如同一本书的目录&#xff0c;会直接告诉你想看的章节是在第几页。不同的是&#xff0c;书是以页为单位的&#x…

软考 - 数据结构与算法

数据结构 线性结构 线性表 存储结构 顺序存储&#xff1a;用一组地址连续的存储单元 依次存储线性表中的数据元素&#xff0c;使得逻辑上相邻的元素物理上也相邻。 链式存储&#xff1a;存储各数据元素的结点的地址并不要求是连续的&#xff0c;数据元素逻辑上相邻&#xff…

提升Mac运行速度的三大方法

任何一部电子设备在使用多年之后都会出现性能下降的问题&#xff0c;苹果的Mac计算机自然也不例外。当你发现Mac运行缓慢&#xff0c;因为有太多文件或缓存垃圾将Mac的运行速度拖了下来。 要想提高生活和工作效率&#xff0c;必须对Mac进行优化&#xff0c;提升一下Mac 的使用性…