Netty 入门 — Bootstrap,一切从这里开始

news2024/12/27 11:43:29

上篇文章(Netty 入门 — 要想掌握 Netty,你必须知道它的这些核心组件)大明哥阐述了 Netty 的整体结构,从这篇文章开始大明哥就将这个整体进行拆分讲解,今天是第一个核心组件:Bootstrap。

一句话来概括 Bootstrap,无论是 Netty 的客户端还是服务端程序,都是从这里开始的。

在Netty 入门 — 亘古不变的Hello World一文的 demo 中,我们首先创建一个 ServerBootstrap 对象(服务端)或者 Bootstrap 对象(客户端),然后调用他们的各个方法进行组装整个 Netty 服务端和客户端,从代码中我们可以看出这两个类是一个辅助类,用来辅助服务端或者客户端初始化和启动的。

Bootstrap 基本介绍

Bootstrap 其实我们理解为一个工厂,我们利用这个工厂可以完成客户端、服务端的初始化,构造一个 Bootstrap 对象后,我们调用其方法为服务端或者客户端设置相关组件和参数,如下:

Bootstrap bootstrap = new Bootstrap()
                    .group(new NioEventLoopGroup())
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.SO_REUSEADDR,true)
                    ...

这个过程是不是非常想一个构造器模式?其实我们真的将其理解为一个构造器,它就是一个构造服务端、客户端的构造器。

那我们是不是可以不使用 Bootstrap 来完成服务端和客户端的初始化呢?其实是可以的,只不过非常麻烦,既然 Netty 为我们提供一个这么好用的工厂类我们为什么不用呢?

启动器有两个,一个是客户端,一个是服务端,如下:

两个类的配置方式大致相同,我们以 ServerBootstrap 为介绍对象。

ServerBootstrap 核心方法

我们使用 ServerBootstrap 来启动服务端,配置流程如下:

对应的代码如下:

// 创建 ServerBootstrap 对象
ServerBootstrap bootstrap = new ServerBootstrap();
// 设置 EventLoopGroup 线程组
bootstrap.group(new NioEventLoopGroup());
// 设置 Channel 类型
bootstrap.channel(NioServerSocketChannel.class);
//配置 option 参数
bootstrap.option(ChannelOption.SO_REUSEADDR,false);
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);

// 定义处理器 Handler
bootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
    @Override
    protected void initChannel(NioSocketChannel ch) throws Exception {
        // 解码
        ch.pipeline().addLast(new StringDecoder());

        ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                System.out.println(ctx.channel() + ",hello world");
            }

            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                System.out.println(new Date() + ":" + msg);
            }
        });
    }
});
// 绑定 8081 端口
ChannelFuture channelFuture = bootstrap.bind(8081).sync();

// 监听关闭事件
// 这里会一直阻塞,知道 channel 关闭
ChannelFuture closeFuture=  channelFuture.channel().closeFuture();
closeFuture.sync();

设置 EventLoopGroup 线程组

调用 group() 可以设置 EventLoopGroup 的线程组,该线程组其实就是 Reactor 的线程组,我们知道 Netty 是基于 Reactor 模式的,group() 就是设置 Reactor 模式的线程组。

我们知道 Reactor 模式,线程组有两个:

  • BossGroup:服务器连接监听线程组,该线程组专门用来处理客户端的连接请求
  • WorkGroup:工作线程组,即业务处理线程组,用来处理每一条连接的数据收发的线程组。

ServerBootstrap 提供了两个 group() 方法用来设置线程组:

  • group(EventLoopGroup parentGroup, EventLoopGroup childGroup):设置 BossGroup 和 WorkGroup。
  • group(EventLoopGroup group):这里只配置了一个线程组,也就是 BossGroup 和 WorkGroup 共用一个线程组。

注:有小伙伴如果对 Reactor 模式不是很理解,可以看看大明哥这篇文章:【死磕 NIO】— Reactor 模式就一定意味着高性能吗?

设置 Channel 类型

在 NIO 中 Channel 是通信的根本,我们收发数据都是基于 Channel 来实现的,对于 Netty 来说 Channel 也是它通信的通道,不过 Netty 不仅仅只支持 NIO 模式,还有 OIO 。

调用 channel() 方法即可设置通道的 IO 类型。

Netty 支持的 IO 类型有如下几种:

  • NioSocketChannel:异步的客户端 TCP Socket 连接.
  • NioServerSocketChannel:异步的服务器端 TCP Socket 连接.
  • NioDatagramChannel:异步的 UDP 连接
  • NioSctpChannel:异步的客户端 Sctp 连接.
  • NioSctpServerChannel:异步的 Sctp 服务器端连接.
  • OioSocketChannel:同步的客户端 TCP Socket 连接.
  • OioServerSocketChannel:同步的服务器端 TCP Socket 连接.
  • OioDatagramChanne:同步的 UDP 连接
  • OioSctpChanne:同步的 Sctp 服务器端连接.
  • OioSctpServerChannel:同步的客户端 TCP Socket 连接.

从上面可以看出,Netty 不仅仅只支持 TCP 协议,还支持 UDP 、STCP 协议,同时每种协议都有 NIO 和 OIO 模式,从 Netty 支持的通道类型就可以看出,Netty 功能有多么强大了。

配置 Option 参数

调用 option() 可以设置 Channel 相关的参数,其实 ServerBootstrap 还有一个 childOption() 方法。两个方法的区别是:

  • option() :是给 parent Channel 设置参数的
  • childOption() :是给 child Channel 设置参数的。

为什么这里会有一个 parent Channel 和 child Channel 呢?首先我们需要明确一点,Channel 是 Socket 连接的一个抽象,我们可以理解它对 Socket 做了一些封装。当 Netty 建立一个连接后,它会为该连接 new 一个 Channel 实例。同时,它也有了父子的概念了,服务端监听的 Channel 叫做 parent Channel,对应每一个 Socket 连接的 Channel 叫做 child Channel。其实我们从设置 EventLoopGroup 的时候就可以看出,group(EventLoopGroup parentGroup, EventLoopGroup childGroup)

可以设置的参数比较多,之类列几个常见的:

  • ChannelOption.CONNECT_TIMEOUT_MILLIS:客户端建立连接时,如果超过指定的时间仍未连接,则抛出 timeout 异常。
  • ChannelOption.SO_KEEPALIVE:是否开启 TCP 底层心跳机制,true 表示开启。
  • ChannelOption.TCP_NODELAY:是否启用 Nagle 算法,true 表示开启。开启可能会对消息的实时性有影响,因为为了提升效率, Nagle 算法会将一些较小的数据包收集后再进行发送,这样就会造成我们的消息有延迟。所以如果实时性要求高的话,一般不建议开启。
  • ChannelOption.SO_RCVBUF:设置接收缓冲区的大小
  • ChannelOption.SO_SNDBUF:设置发送缓冲区的大小,一般 SO_RCVBUF 和 SO_SNDBUF 不建议手动设置,因为操作系统会根据当前占用,进行自动的调整。

其他参数,大明哥就不一一介绍了,有兴趣的小伙伴可以自行研究,大明哥在后面的 Netty 调优部分会对这些字段进行详细讲解的。

装配流水线

装配流水线其实就是配置处理的 Handler,ChannelPipeline 负责协调这些 Handler,它是 Netty 处理请求的责任链,该链上每个节点都是 ChannelHandler,而这些 ChannelHandler 就是用来处理这些请求的。

ServerBoostrap 提供了 childHandler() 方法用来装配这些 ChannelHandler ,用来组装成一个处理请求的流水线。我们传递一个 ChannelInitializer 实例,ChannelInitializer 是一个特殊的 ChannelHandler,它主要是为我们提供了一个简单的工具,用于在某个 Channel 注册到 EventLoop 后,对这个 Channel 执行一些初始化操作。

ChannelInitializer 是一个抽象类,我们需要实现它的 initChannel(),在该方法中我们通过 ChannelPipeline 来完成流水线的装配。

ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){//});

ChannelHandler 有两种,分别是 ChannelInboundHandler 和 ChannelOutboundHandler,一个是处理进站,一个处理出站,通俗理解就是,ChannelInboundHandler 处理读入 IO 请求,ChannelOutboundHandler 处理写出 IO 请求。

我们调用 ch.pipeline().addLast() 方法可以装配一个 ChannelHandler 链,结构如下:

当读请求进来时,传播链从 head 出发,依次经过 InboundHandlerA,InboundHandlerB,InboundHandlerC,InboundHandlerD,最后在 Tail 终止。而写出 IO 请求则相反,它是从 Tail 出发,依次经过 OutboundHandlerB,OutboundHandlerA,最后在 Head 终止。

最后

Bootstrap,在 Netty 中是一个比较简单的且容易理解的组件,它仅仅只是服务端、客户端启动的引导器,通过调用相对应的方法,为不同的功能设置不同的组件,组装成一个可以运行的完整的服务端或者客户端。

当然,Bootstrap 还有一些方法大明哥没有介绍,但并妨碍我们去理解 Netty,去理解这些组件,最后,大明哥将 Bootstrap 的方法列举出来,感兴趣的小伙伴可以尝试着实验下:

AbstractBootstrap

方法描述
group设置用于处理所有事件的 EventLoopGroup
channel指定服务端或客户端的 Channel
channelFactory如果引导没有指定 Channel,那么可以指定 ChannelFactory 来创建 Channel
localAddress指定 Channel 需要绑定的本地地址,如果不指定,则将由系统随机分配一个地址
remoteAddress设置 Channel 需要连接的远程地址
attr指定新创建的 Channel 的属性值
handler设置添加到 ChannelPipeline 中的 ChannelHandler
connect连接到远程主机,返回 ChannelFuture,用于连接完成的回调
option设置 Channel 参数
register创建一个 Channel,并注册到 Eventloop
bind绑定端口

ServerBoostrap

方法描述
childOption为 child Channel 设置参数
childAttr为 child Channel 设置属性值
childHandler添加 ChannelHandler 处理器

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

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

相关文章

在Telegram营销后该如何进行客户管理

与目标客户进行接触之后&#xff0c;我们就要开始考虑后续怎么去销售自己的产品。这个过程可能是很漫长的&#xff0c;我们需要经常去联系对方&#xff0c;回答对方的疑问。但是现实中通常一个员工会手握多个账号&#xff0c;很难及时知道每个账号的信息&#xff0c;管理客户成…

基于Python实现的快速的仿手写文字的图片生成器项目源码

Quick Hand &#x1f4dd; 介绍 快速的仿手写文字的图片生成器。 完整代码下载地址&#xff1a;基于Python实现的快速的仿手写文字的图片生成器 界面预览&#xff1a; &#x1f52e; 使用说明 原理&#xff1a;首先&#xff0c;在水平位置、竖直位置和字体大小三个自由度上…

数据结构与算法之图: Leetcode 133. 克隆图 (Typescript版)

克隆图 https://leetcode.cn/problems/clone-graph/description/ 描述 给你无向 连通 图中一个节点的引用&#xff0c;请你返回该图的 深拷贝&#xff08;克隆&#xff09;。 图中的每个节点都包含它的值 val&#xff08;int&#xff09; 和其邻居的列表&#xff08;list[No…

iPhone怎么屏蔽短信?屏蔽骚扰短信,就用这2招!

如今&#xff0c;信息互联网快速发展&#xff0c;为我们的生活带来了很多好处。但同时我们也随时面临着隐私泄露的风险。大家的手机上是不是经常会收到很多骚扰信息&#xff1f;“叮咚~”&#xff0c;你以为手机收到了重要的信息&#xff0c;但其实只是一则毫无用处的短信。 这…

git提交代码产生冲突的解决方法

1.产生冲突的提示 2.解决&#xff1a;根据你的情况选择保留当前更改或者远程仓库 点击Accept Current Change 或者Accept incoming change 即可

STM32-LCD中英文显示及应用

字符编码 由于计算机只能识别0和1&#xff0c;所以文字需要以0和1的形式在计算机内继续存储&#xff0c;故需要对文字进行编码。最简单的编码就是ASCII码。 ASCII码&#xff08;8位&#xff09; ASCII码分两部分&#xff1a; 0~31&#xff1a;控制字符或通讯字符。没有特定的图…

庆祝创造力和技术:2023年的1024程序员节

2023年的10月24日已经来临&#xff0c;这意味着我们又迎来了一年一度的程序员节。这是一个属于全球程序员社区的节日&#xff0c;一个庆祝创造力、创新和技术的时刻。无论你是一名职业程序员、技术爱好者&#xff0c;还是对编程世界感兴趣的新手&#xff0c;1024程序员节都是一…

园区组网配置实例

项目拓扑与项目需求 项目需求 某企业网络组网如下&#xff1a;vlan10属于办公网络&#xff0c;vlan20外来人员访客网络&#xff0c;vlan30 属于云桌面网络。还包括公共服务器&#xff0c;所属vlan为100. 需要实现如下需求&#xff1a; vlan10 和vlan20 和vlan100属于相同网段…

用Rust和cURL库做一个有趣的爬虫

以下是一个使用 Rust 和 cURL 库的下载器程序&#xff0c;用于从wechat下载音频。此程序使用了 [/get_proxy] 提供的代码。 extern crate curl;use std::io::{self, Read}; use std::process::exit; use curl::easy::Easy;fn main() {let url "https://www.wechat.com/au…

图片base64说明

将一张图片数据编码成一串字符串&#xff0c;使用该字符串代替图片地址url 前端页面中常见的base64图片引入方式&#xff1a; 优点&#xff1a; ①、base64格式的图片是文本格式&#xff0c;占用内存小&#xff0c;转换后的大小比例大概为1/3&#xff0c;降低了资源服务器的…

C#开发的OpenRA游戏之金钱系统(4)

C#开发的OpenRA游戏之金钱系统(4) 前面已经分析怎么样找到资源收割的位置,接着下来就是怎么样移动到资源的坐标,以及怎么样进行收割资源。现在就来分析这个相关的代码,这些功能都在文件HarvestResource.cs里,声明了一个类HarvestResource。 类HarvestResource就是在确定…

SAP从入门到放弃系列之QM样本确定-采样过程的采样方案

目录 一、概述二、AQL概念三、系统操作 一、概述 样本确定过程中可以有百分比、固定样本、参考采样方案&#xff08;Sample Schema&#xff09;三种方式。其中百分比、固定样本的采样方案是比较号理解的&#xff0c;但是参考采样方案中相对逻辑更复杂&#xff0c;再参考采样方…

微信视频号视频下载新手用户该如何下载保存到本地?三个方法教会你

微信视频号是一个非常受欢迎的平台&#xff0c;提供了各种有趣和有价值的视频内容。想要将这些视频下载保存到本地可以帮助你随时观看和分享。下面&#xff0c;我将为你介绍几种方法。 方法一&#xff1a;使用第三方应用 1. 在微信客户端中搜索并获取一个一个支持微信视频号下…

YOLOV8目标检测——最全最完整模型训练过程记录

文章目录 前言1 下载yolov8&#xff08;[网址](https://github.com/ultralytics/ultralytics)&#xff09;2 配置conda环境3 用pycharm打开文件3 训练自己的YOLOV8数据集4 run下运行完了之后没有best.pt文件5 导出为onnx文件6 yolov8应用完整案例&#xff08;免费且包含源代码、…

AHK v2中一个问号两个问号代表啥意思

一个问号&#xff1a; 函数入参以一个问号为后缀表示传参时省略该参数则相应变量就unset。 两个问号&#xff1a; 合并运算符. 如果左操作数(必须是一个变量) 有一个值, 它就成为结果, 并跳过右分支. PS. 三元操作符

【30】c++设计模式——>状态模式

状态模式概述 状态模式是一种行为型设计模式&#xff0c;它可以让一个对象在其内部状态发生变化时更改其行为。通过将每个状态封装成一个独立的类&#xff0c;我们可以使状态之间互相独立&#xff0c;并且使得状态的切换变得更加灵活、可扩展。&#xff08;多个状态之间可以相…

CSS边框线段样式

让我为大家介绍一下边框样式吧&#xff01;如果大家想更进一步了解边框的使用&#xff0c;可以阅读这一篇文章&#xff1a;CSS边框border 属性描述none没有边框,即忽略所有边框的宽度(默认值)solid边框为单实线dashed边框为虚线dotted边框为点线double边框为双实线 代码演示&…

XPS就是分一下峰没你想的那么简单!-科学指南针

还记得前一段时间的一篇刷屏的经典文章吗! 林雪平大学(Linkping University)的Grzegorz Greczynski和Lars Hultman二人发表观点性文章&#xff0c;对诺奖得主K. Siegbahn推荐的XPS校准方法可能存在的问题进行了阐述与批评&#xff0c;并提出建议。文章原标题为“Compromising S…

XPS测试能谱数据如何处理-科学指南针

本文将分三部分介绍如何用ORIGIN软件处理XPS测试能谱数据&#xff1a; 1、多元素谱图数据处理 2、剖面分析数据处理 3、复杂谱图的解叠 一、多元素谱图的处理&#xff1a; 1、将ASC码文件用NOTEPAD打开&#xff1a; 2、复制Y轴数值。打开ORIGIN&#xff0c;将Y轴数据粘贴到B&am…

栈和队列(8.4)

1. 栈 1.1 栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。 栈中的数据元素遵守后进先出 LIFO &#xff08; Last In First Out &#xff09;…