2024-04-04  阅读(97)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/mianshi/baodian/detail/1424617706

回答

在 Netty 中,Bootstrap 主要用于配置和引导启动客户端或服务端。它负责将不同的 Netty 的核心组件如 EventLoopGroup、Channel、ChannelPipeline 等组装在一起,以简化网络应用程序的配置和启动过程。通过 Bootstrap,我们可以以最少的代码和配置,快速地启动一个高性能、可伸缩的网络应用程序。

其主要作用有如下几个:

  • 设置 EventLoopGroup 线程组:提供 group() 设置 EventLoopGroup 的线程组,该线程组其实就是 Reactor 的线程组。
  • 设置 Channel 类型:提供 channel() 设置 Channel 的 I/O 类型。Netty 提供了多种 I/O 类型。
  • 配置 Option 参数:提供 option() 设置 Channel 相关的参数。
  • 装配流水线:提供 childHandler() 或者 handler() 用来装配 ChannelHandler ,这些 ChannelHandler 用来组装成一个处理请求的流水线。

详情

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 共用一个线程组。

设置 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 不建议手动设置,因为操作系统会根据当前占用,进行自动的调整。

其他参数,大明哥就不一一介绍了,有兴趣的小伙伴可以自行研究。

装配流水线

装配流水线其实就是配置处理的 ChannelHandler,ChannelPipeline 负责协调这些 ChannelHandler,它是 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 处理器
阅读全文
  • 点赞