学习Netty BootStrap的核心知识,成为网络编程高手!

news2024/10/7 4:24:01

0 定义

深入 ChannelPipeline、ChannelHandler 和 EventLoop 后,如何将这些部分组织起来,成为可运行的应用程序?

引导(Bootstrapping)!引导一个应用程序是指对它进行配置,并使它运行起来的过程—尽管该过程的具体细节可能并不如它的定义那样简单,尤其是对于一个网络应用程序来说。

和它对应用程序体系架构的分层抽象一致,Netty处理引导的方式使你的【应用程序的逻辑或实现】和【网络层】相

隔离,而无论它是客户端还是服务器。所有的框架组件都将会在后台结合在一起并启用。引导是我们一直以来都在组装的完整拼图(Netty 的核心概念以及组件,也包括如何完整正确地组织并且运行一个 Netty 应用程序)中缺失的那一块。当你把它放到正确的位置上时,你的Netty应用程序就完整了。

1 Bootstrap 类

引导类的层次结构包括一个抽象父类和两个具体的引导子类:

相比于将具体的引导类分别看作用于服务器、客户端的引导,记住它们的本意是用来支撑不同的应用程序的功能的更有裨益,即:

  • 服务器致力于使用一个父 Channel 接受来自客户端的连接,并创建子 Channel 用于它们之间的通信

  • 而客户端将最可能只需要一个单独的、没有父 Channel 的 Channel 用于所有的网络交互

    正如同我们将看到的,这也适用于无连接的传输协议,如 UDP,因为它们并不是每个连接都需要一个单独的 Channel

客户端和服务器两种应用程序类型之间通用的引导步骤由 AbstractBootstrap 处理,而特定于客户端或服务器的引导步骤则分别由 Bootstrap 或 ServerBootstrap 处理。

接下来将详细地探讨这两个类,首先从不那么复杂的 Bootstrap 类开始。

1.1 为何引导类是 Cloneable

有时可能需要创建多个类似或完全相同配置的Channel。为支持这种模式而又无需为每个 Channel 都创建并配置一个新的引导类实例, AbstractBootstrap 被标记为 Cloneable。在一个已配置完成的引导类实例上调用clone()方法将返回另一个可立即使用的引导类实例。

这种方式只会创建引导类实例的EventLoopGroup的浅拷贝,所以,【被浅拷贝的 EventLoopGroup】将在所有克隆的Channel实例之间共享。这能接受,因为通常这些克隆的Channel的生命周期都很短暂,一个典型场景:创建一个Channel以进行一次HTTP请求。

AbstractBootstrap 类的完整声明:

// 子类型 B 是其父类型的一个类型参数,因此可以返回到运行时实例的引用以支持方法的链式调用(流式语法)
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {

其子类的声明:

public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
}

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
}

2 引导客户端和无连接协议

Bootstrap类被用于客户端或使用了无连接协议的应用程序。表8-1很多继承自AbstractBootstrap:

表 8-1:Bootstrap类的API

2.1 引导客户端

Bootstrap 类负责为客户端和使用无连接协议的应用程序创建 Channel,如图 8-2:

图 8-2:引导过程

代码清单 8-1 引导了一个使用 NIO TCP 传输的客户端。

代码清单 8-1:引导一个客户端

EventLoopGroup group = new NioEventLoopGroup();
// 创建一个Bootstrap类的实例,以创建和连接新的客户端
Channel
Bootstrap bootstrap = new Bootstrap();

/**
 * 使用流式语法;这些方法(除了connect()方法)将通过每次方法调用所返回的对 Bootstrap 实例的引用
 * 链接在一起。
 */

// 设置 EventLoopGroup,提供用于处理 Channel 事件的 EventLoop
bootstrap.group(group)
  			 // 指定要使用的 Channel 实现
        .channel(NioSocketChannel.class)
  			 // 设置用于 Channel 事件和数据的ChannelInboundHandler
        .handler(new SimpleChannelInboundHandler<ByteBuf>() {
            @Override
            protected void channelRead0(
                    ChannelHandlerContext channelHandlerContext,
                    ByteBuf byteBuf) throws Exception {
                System.out.println("Received data");
            }
        });
// 连接到远程主机
ChannelFuture future = bootstrap.connect(new InetSocketAddress("www.JavaEdge.com", 80));
future.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture channelFuture) throws Exception {
        if (channelFuture.isSuccess()) {
            System.out.println("Connection established");
        } else {
            System.err.println("Connection attempt failed");
            channelFuture.cause().printStackTrace();
        }
    }
});

2.2 Channel 和 EventLoopGroup 的兼容性

代码清单 8-2 所示目录清单来自 io.netty.channel 包。从包名及类名前缀可见,对 NIO 及 OIO 传输,都有相关EventLoopGroup 和 Channel 实现。

相互兼容的 EventLoopGroup 和 Channel:

必须保持这种兼容性,不能混用具有不同前缀的组件,如 NioEventLoopGroup 和 OioSocketChannel。代码清单 8-3 展示了试图这样做的一个例子

EventLoopGroup group = new NioEventLoopGroup();
// 创建一个新的 Bootstrap类的实例,以创建新的客户端Channel
Bootstrap bootstrap = new Bootstrap();
// 指定一个适用于 NIO 的 EventLoopGroup 实现
bootstrap.group(group)
  			 // 指定一个适用于OIO 的 Channel 实现类
        .channel(OioSocketChannel.class)
  			 // 设置一个用于处理 Channel的 I/O 事件和数据的ChannelInboundHandler
        .handler(new SimpleChannelInboundHandler<ByteBuf>() {
            @Override
            protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf)
                    throws Exception {
                System.out.println("Received data");
            }
        });
// 尝试连接到远程节点
ChannelFuture future = bootstrap.connect(new InetSocketAddress("www.JavaEdge.com", 80));
future.syncUninterruptibly();

这段代码将会导致 IllegalStateException,因为它混用了不兼容的传输

Exception in thread "main" java.lang.IllegalStateException: incompatible event loop type: io.netty.channel.nio.NioEventLoop
	at io.netty.channel.AbstractChannel$AbstractUnsafe.register(AbstractChannel.java:462)

IllegalStateException

引导的过程中,在调用 bind()或 connect()前,必须调用以下方法来设置所需的组件:

  • group()
  • channel()或者 channelFactory()
  • handler()

若不这样做,将导致 IllegalStateException。对 handler()方法的调用尤其重要,因为它需要配置好 ChannelPipeline。

3 引导服务器

从 ServerBootstrap API 概要视图开始对服务器引导过程的概述。然后,探讨引导服务器过程中所涉及的几个步骤及几个相关的主题,包含从一个 ServerChannel 的子 Channel 中引导一个客户端这样的特殊情况。

3.1 ServerBootstrap 类

表 8-2 列出了 ServerBootstrap 类的方法:

表8-2 ServerBootstrap类的方法

3.2 引导服务器

表 8-2 中列出一些表 8-1 不存在的方法:childHandler()、childAttr()和 childOption()。这些调用支持特别用于服务器应用程序的操作。

ServerChannel 的实现负责创建子 Channel,这些子 Channel 代表了已被接受的连接。因此,负责引导 ServerChannel 的 ServerBootstrap 提供了这些方法,以简化将设置应用到已被接受的子 Channel 的 ChannelConfig 的任务。

图 8-3 展示 ServerBootstrap 在 bind()方法被调用时创建了一个 ServerChannel,并且该 ServerChannel 管理了多个子 Channel。

图 8-3:ServerBootstrap 和 ServerChannel

代码8-4 实现图 8-3 中所展示的服务器的引导过程:

package io.netty.example.cp8;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.net.InetSocketAddress;

/**
 * 代码清单 8-4 引导服务器
 *
 * @author JavaEdge
 * @date 2023/5/20
 */
public class Demo84 {

    public static void main(String[] args) {
        NioEventLoopGroup group = new NioEventLoopGroup();
        // 创建 ServerBootstrap
        ServerBootstrap bootstrap = new ServerBootstrap();
        // 设置 EventLoopGroup,其提供了用于处理Channel 事件的EventLoop
        bootstrap.group(group)
                // 指定要使用的 Channel 实现
                .channel(NioServerSocketChannel.class)
                // 设置用于处理已被接受的子Channel的I/O及数据的 ChannelInboundHandler
                .childHandler(new SimpleChannelInboundHandler<ByteBuf>() {
                    @Override
                    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
                        System.out.println("Received data");
                    }
                });
        // 通过配置好的ServerBootstrap的实例绑定该Channel
        ChannelFuture future = bootstrap.bind(new InetSocketAddress(8080));
        future.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                if (channelFuture.isSuccess()) {
                    System.out.println("Server bound");
                } else {
                    System.err.println("Bound attempt failed");
                    channelFuture.cause().printStackTrace();
                }
            }
        });
    }
}

4 从 Channel 引导客户端

服务器正在处理一个客户端请求,该请求需要它充当第三方系统的客户端。当一个应用程序(如一个代理服务器)必须要和现有的系统(如 Web 服务或数据库)集成时,就可能发生这种情况。此时,将需要从已被接受的子 Channel 中引导一个客户端 Channel。

可按 8.2.1 节中所描述的方式创建新的 Bootstrap 实例,但是这并不是最高效的解决方案,因为它要求你为每个新创建的客户端 Channel 定义另一个 EventLoop,会产生额外的线程,以及在已被接受的子 Channel 和客户端 Channel 之间交换数据时不可避免的上下文切换。

更好的解决方案

将已被接受的子 Channel 的 EventLoop 传递给 Bootstrap 的 group()方法来共享该 EventLoop。因为分配给 EventLoop 的所有 Channel 都使用同一线程,所以这避免了:

  • 额外的线程创建
  • 前面所提到的相关的上下文切换

该共享的解决方案图:

图 8-4:在两个 Channel 之间共享 EventLoop

实现 EventLoop 共享涉及通过调用 group()方法来设置 EventLoop,如代码8-5:

package io.netty.example.cp8;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.net.InetSocketAddress;

/**
 * 代码清单 8-5 引导服务器
 *
 * @author JavaEdge
 * @date 2023/5/20
 */
public class Demo85 {

    public static void main(String[] args) {
        // 创建 ServerBootstrap 以创建ServerSocketChannel,并绑定它
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        // 设置 EventLoopGroup,其将提供用以处理 Channel 事件的 EventLoop
        serverBootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup())
                // 指定要使用的 Channel 实现
                .channel(NioServerSocketChannel.class)
                // 设置用于处理已被接受的 子 Channel 的 I/O 和数据的ChannelInboundHandler
                .childHandler(
                        new SimpleChannelInboundHandler<ByteBuf>() {
                            ChannelFuture connFuture;

                            @Override
                            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                // 创建一个 Bootstrap 类的实例以连接到远程主机
                                Bootstrap bootstrap = new Bootstrap();
                                // 指定 Channel 的实现
                                bootstrap.channel(NioSocketChannel.class)
                                        // 为入站 I/O 设置 ChannelInboundHandler
                                        .handler(
                                                new SimpleChannelInboundHandler<ByteBuf>() {
                                                    @Override
                                                    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
                                                        System.out.println("Received data");
                                                    }
                                                });
                                // 使用与分配给已被接受的子Channel相同的EventLoop
                                bootstrap.group(ctx.channel().eventLoop());
                                // 连接到远程节点
                                connFuture = bootstrap.connect(new InetSocketAddress("www.manning.com", 80));
                            }

                            @Override
                            protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
                                if (connFuture.isDone()) {
                                    // 当连接完成时,执行一些数据操作(如代理)
                                    // do something with the data
                                    System.out.println();
                                }
                            }
                        });
        // 通过配置好的ServerBootstrap绑定该 ServerSocketChannel
        ChannelFuture future = serverBootstrap.bind(new InetSocketAddress(8080));
        future.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                if (channelFuture.isSuccess()) {
                    System.out.println("Server bound");
                } else {
                    System.err.println("Bind attempt failed");
                    channelFuture.cause().printStackTrace();
                }
            }
        });
    }
}

这一节中所讨论的主题以及所提出的解决方案都反映了编写 Netty 应用程序的一个一般准则:尽可能复用 EventLoop,以减少线程创建所带来的开销。

5 在引导过程中添加多个 ChannelHandler

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

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

相关文章

AcWing算法提高课-1.3.8多重背包问题 III

宣传一下算法提高课整理 <— CSDN个人主页&#xff1a;更好的阅读体验 <— 本题链接&#xff08;AcWing&#xff09; 点这里 题目描述 有 N N N 种物品和一个容量是 V V V 的背包。 第 i i i 种物品最多有 s i s_i si​ 件&#xff0c;每件体积是 v i v_i vi​…

我们到底该怎么入门网络安全?一般人还真不行

我之前写了不少网络安全技术相关的故事文章&#xff0c;不少读者朋友知道我是从事网络安全相关的工作&#xff0c;于是经常有人在微信里问我&#xff1a;&#xff08;文末入门福利&#xff09; 我刚入门网络安全&#xff0c;该怎么学&#xff1f; 要学哪些东西&#xff1f; …

linux系统打包压缩与搜索命令

1、tar命令 在linux系统中&#xff0c;常见的文件格式比较多&#xff0c;其中主要使用使用的tar和zip来压缩文件&#xff0c;tar命令的参数比较多&#xff0c;这里呢我一般只会记得几个比较常用的。 -c创建压缩文件-x解开压缩文件-z用Gzip压缩或解压-v显示压缩或者解压的过程-…

C语言—自定义类型(结构体、枚举、联合)

自定义类型 结构体结构体的声明特殊的声明结构的自引用结构体变量的定义和初始化结构体内存对齐修改默认对齐数offsetof宏 结构体传参 位段位段的定义&#xff08;声明&#xff09;位段的内存分配位段的跨平台问题位段的应用 枚举枚举类型的定义及使用枚举的优点 联合&#xff…

第02章 变量与运算符

一 关键字&#xff08;keyword&#xff09; 定义&#xff1a;被Java语言赋予了特殊含义&#xff0c;用做专门用途的字符串&#xff08;或单词&#xff09; HelloWorld案例中&#xff0c;出现的关键字有 class、public 、 static 、 void 等&#xff0c;这些单词已经被Java定义…

Linux设备驱动模型(二)

基于linux-3.14.16 设备模型&#xff08;LDM&#xff09;包括&#xff0c;总线、驱动、设备 一、总线及其api 1、注册一个总线 以i2c总线为例&#xff0c;下面基本表现出了注册一个总线的过程。 1、定义一个总线bus_type&#xff0c;填充几个回调 2、调用bus_register注册总线…

【STM32系列】硬件介绍及固件安装

【STM32系列】硬件介绍及固件安装 硬件介绍应用领域 固件安装进入DFU模式安装固件退出DFU&#xff0c;进入U盘模式 演讲稿 欢迎收看由咸鱼菌工作室出品的STM32系列教程。本篇内容主要是设备介绍及环境安装 硬件介绍 本系列所选硬件ZTMR(STM32)开发板 &#xff0c;选用STM32芯片…

新手如何学习挖SRC漏洞?【网络安全】

前言 有不少阅读过我文章的伙伴都知道&#xff0c;我从事网络安全行业已经好几年&#xff0c;积累了丰富的经验和技能。在这段时间里&#xff0c;我参与了多个实际项目的规划和实施&#xff0c;成功防范了各种网络攻击和漏洞利用&#xff0c;提高了安全防护水平。 也有很多小…

【ProtoBuf】1.初识ProtoBuf

————————————每一个不曾起舞的日子都是对生命的辜负。 初识ProtoBuf 一. 序列化概念二. ProtoBuf1. 什么是ProtoBuf2. ProtoBuf的特点3. ProtoBuf进行序列化反序列化的流程 三. 本章总结 一. 序列化概念 日常生活中&#xff0c;手机上收到的语音消息在网络中不能直…

多线技术:TDI时间延迟积分

多线技术&#xff1a;TDI时间延迟积分 在比较暗的环境下拍照&#xff0c;清晰成像的条件&#xff0c;是CMOS芯片获取足够的光。而获得足够光的方法有&#xff1a; 方案1.增加单位时间的光照强度&#xff08;光源、大镜头收集光&#xff09; 方案2.增加CMOS感光时长&#xf…

国潮还能怎么玩?小红书用户画像速看!

所谓“国潮”&#xff0c;概括来说就是“国风潮流”。主要有两层含义&#xff1a;其一&#xff0c;有中国文化和传统的基因&#xff1b;其二&#xff0c;能将传统文化与时下潮流相融合&#xff0c;使产品更具时尚感。在“国潮”元年之前&#xff0c;“国潮”大多指狭义上的特定…

macOS Ventura 13.5beta (22G5027e)发布

系统介绍 黑果魏叔 5 月 20 日消息&#xff0c;苹果今日向 Mac 电脑用户推送了 macOS 13.5 开发者预览版 Beta 更新&#xff08;内部版本号&#xff1a;22G5027e&#xff09;&#xff0c;本次更新距离上次发布隔了 17 天。 macOS Ventura 带来了台前调度、连续互通相机、Face…

四象限法进程调度

周二收到一篇推送 一次云上网络毫秒级的优化与实践&#xff0c;很有意义的实践和探索&#xff0c;建议阅读&#xff0c;文章不长&#xff0c;没有冗长的源码分析&#xff0c;结论很清晰。 谈谈我的看法。 多少有种感觉&#xff0c;Linux 越来越像个响应系统而不是服务器。 虚…

TimesNet:用于一般时间序列分析的时间二维变化模型(代码解析)

前言 TimesNet&#xff1a;用于一般时间序列分析的时间二维变化模型论文下载地址&#xff0c;Github项目地址&#xff0c;论文解读系列本文针对TimesNet模型参数与模型架构开源代码进行讲解&#xff0c;本人水平有限&#xff0c;若出现解读错误&#xff0c;欢迎指出开源代码中…

回归分析-简单线性回归推导

回归分析-简单线性回归模型 在大数据分析中&#xff0c;回归分析是一种预测性的建模技术&#xff0c;它研究的是因变量&#xff08;目标&#xff09;和自变量&#xff08;预测器&#xff09;之间的关系。这种技术通常用于预测分析&#xff0c;时间序列模型以及发现变量之间的因…

浏览器免费安装ChatGPT插件与国内免费ChatGPT分享

文章目录 一、什么是ChatGPT&#xff1f;ChatGPT的功能&#xff1a; 二、如果在Edge上安装chatgpt插件三、国内免费ChatGPT四、ChatGPT程序员的影响五、Chatgp能取代程序员吗&#xff1f; 一、什么是ChatGPT&#xff1f; ChatGPT是一种基于自然语言处理的机器学习算法&#xf…

【牛客刷题】 选择题整理day3~day4

在知识的海洋里彻底疯狂&#xff01;&#xff01;&#xff01; 文章目录 1. try-catch-finally2. 方法调用3. 接口4. 1. try-catch-finally try-catch-finally的执行机制是 try块是不能被省略的&#xff0c;用于包含可能会抛出异常的代码&#xff0c;如果没有try块&#xff0c…

Linux之DNAT策略及应用与tcpdump抓包

目录 一、DNAT的介绍 二、DNAT实验设计 三、DNAT具体实验步骤操作 第一步&#xff1a;配置好网卡与环境 第二步&#xff1a;web服务器安装httpd服务&#xff0c;且开启httpd服务 第三步&#xff1a;对网关服务器进行操作 1.设置路由转发 2.设置SNAT 四、tcpdump抓包工具…

yooasset+hybridclr在android,ios端热更新测试

Hybridclr+YooAsset+Unity Run android+iOS 这个工程是用来学习YooAsset和Hybridcl,来做unity资源和代码热更新, 实现了android 和ios 双端,跑通。 源码在文章最后。 版本 很新的版本 用到的YooAsset 1.4.13 用到的HybridCLR 2.4.2 yooAsset git网址 https://github.co…

CVTE C++软开全程面试(一面、二面、群面、HR面)

一面&#xff0c;面了一个钟&#xff0c;问了很多问题&#xff0c;大部分是计算机的基础知识&#xff0c;我也只能记录下一部分。 C的继承问题&#xff0c;protected成员被public、protected和private继承的情况。 下面是关于protected成员在不同类型继承中的访问权限&#xff…