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

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

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:

2.1 引导客户端

Bootstrap 类负责为客户端和使用无连接协议的应用程序创建 Channel,如图 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 类的方法:

3.2 引导服务器

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


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


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

代码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 都使用同一线程,所以这避免了:

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

该共享的解决方案图:

实现 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

目录
相关文章
|
3月前
|
机器学习/深度学习 算法 数据挖掘
没发论文的注意啦!重磅更新!GWO-BP-AdaBoost预测!灰狼优化、人工神经网络与AdaBoost集成学习算法预测研究(Matlab代码实现)
没发论文的注意啦!重磅更新!GWO-BP-AdaBoost预测!灰狼优化、人工神经网络与AdaBoost集成学习算法预测研究(Matlab代码实现)
137 0
|
2月前
|
JavaScript Java 大数据
基于python的网络课程在线学习交流系统
本研究聚焦网络课程在线学习交流系统,从社会、技术、教育三方面探讨其发展背景与意义。系统借助Java、Spring Boot、MySQL、Vue等技术实现,融合云计算、大数据与人工智能,推动教育公平与教学模式创新,具有重要理论价值与实践意义。
|
2月前
|
监控 前端开发 安全
Netty 高性能网络编程框架技术详解与实践指南
本文档全面介绍 Netty 高性能网络编程框架的核心概念、架构设计和实践应用。作为 Java 领域最优秀的 NIO 框架之一,Netty 提供了异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。本文将深入探讨其 Reactor 模型、ChannelPipeline、编解码器、内存管理等核心机制,帮助开发者构建高性能的网络应用系统。
213 0
|
6月前
|
弹性计算 网络协议 Java
Netty基础—2.网络编程基础二
本文介绍了网络编程的基本概念和三种主要模式:BIO(阻塞IO)、AIO(异步IO)和NIO(非阻塞IO)。BIO模型通过为每个客户端连接创建一个线程来处理请求,适合客户端较少的情况,但在高并发下性能较差。AIO模型通过异步IO操作,允许操作系统处理IO,适合高并发场景,但编码复杂且Linux支持有限。NIO模型通过Selector实现多路复用,适合高并发且性能要求高的场景。文章还详细介绍了NIO中的Buffer、Selector、Channel等核心组件,并提供了NIO的实战开发流程和代码示例。
|
6月前
|
监控 网络协议 Java
Netty基础—1.网络编程基础一
本文详细介绍了网络通信的基础知识,涵盖OSI七层模型、TCP/IP协议族及其实现细节。首先解释了OSI模型各层功能,如物理层负责数据通路建立与传输,数据链路层提供无差错传输等。接着探讨了TCP/IP协议,包括TCP和UDP的特点、三次握手与四次挥手过程,以及如何通过确认应答和序列号确保数据可靠性。还分析了HTTP请求的传输流程和报文结构,并讨论了短连接与长连接概念。 此外,解析了Linux下的IO模型,包括阻塞IO、非阻塞IO、IO复用(select/poll/epoll)、信号驱动IO和异步IO的特点与区别,强调了epoll在高并发场景下的优势及其水平触发和边缘触发两种工作模式。
|
6月前
|
网络协议 算法 Java
Netty基础—3.基础网络协议
本文详细梳理了计算机网络的基础知识,涵盖从物理层到应用层的各层协议及其功能。内容包括七层模型与四层模型对比、IP地址与子网划分、TCP三次握手及四次挥手过程、Socket编程原理、HTTP/HTTPS协议的工作机制等。同时深入探讨了Linux IO模型(阻塞、非阻塞、IO多路复用)及其应用场景,并分析了select、poll、epoll的区别。此外,还涉及Java IO读写的底层流程及同步异步、阻塞非阻塞的概念。这些知识点为理解网络通信和高性能服务器开发提供了全面的理论支持。
|
9月前
|
人工智能 网络协议 IDE
使用通义灵码AI高效学习muduo网络库开发指南
Muduo 是一个基于 C++11 的高性能网络库,支持多线程和事件驱动,适用于构建高效的服务器和应用程序。它提供 TCP/IP 协议支持、异步非阻塞 I/O、定时器、异步日志等功能,并具备跨平台特性。通过 Git 克隆 muduo 仓库并切换至 C++17 分支可开始使用。借助 AI 工具如 Deepseak-v3,用户可以更便捷地学习和理解 Muduo 的核心模块及编写测试用例,提升开发效率。
|
11月前
|
JSON 算法 Java
Nettyの网络聊天室&扩展序列化算法
通过本文的介绍,我们详细讲解了如何使用Netty构建一个简单的网络聊天室,并扩展序列化算法以提高数据传输效率。Netty的高性能和灵活性使其成为实现各种网络应用的理想选择。希望本文能帮助您更好地理解和使用Netty进行网络编程。
157 12
|
网络协议 安全 NoSQL
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-2):scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练、就怕你学成黑客啦!
scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-2):scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练、就怕你学成黑客啦!
|
12月前
|
消息中间件 编解码 网络协议
Netty从入门到精通:高性能网络编程的进阶之路
【11月更文挑战第17天】Netty是一个基于Java NIO(Non-blocking I/O)的高性能、异步事件驱动的网络应用框架。使用Netty,开发者可以快速、高效地开发可扩展的网络服务器和客户端程序。本文将带您从Netty的背景、业务场景、功能点、解决问题的关键、底层原理实现,到编写一个详细的Java示例,全面了解Netty,帮助您从入门到精通。
1380 0