Netty(六)揭开 BootStrap 的神秘面纱
阅读原文时间:2023年07月12日阅读:5

6.1 客户端 BootStrap

6.1.1 Channel 简介

在 Netty 中,Channel 是一个 Socket 的抽象,它为用户提供了关于 Socket 状态(是否是连接还是断开)以及对 Socket

的读写等操作。每当 Netty 建立了一个连接后, 都创建一个对应的 Channel 实例。

除了 TCP 协议以外,Netty 还支持很多其他的连接协议, 并且每种协议还有 NIO(非阻塞 IO)和 OIO(Old-IO, 即传统的

阻塞 IO)版本的区别。不同协议不同的阻塞类型的连接都有不同的 Channel 类型与之对应下面是一些常用的 Channel

类型:

6.1.2 NioSocketChannel 的创建

Bootstrap 是 Netty 提供的一个便利的工厂类, 我们可以通过它来完成 Netty 的客户端或服务器端的 Netty 初始化。

下面我先来看一个例子, 从客户端和服务器端分别分析一下 Netty 的程序是如何启动的。首先,让我们从客户端的代码

片段开始: 

EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//自定义协议解码器
/** 入参有5个,分别解释如下
maxFrameLength:框架的最大长度。如果帧的长度大于此值,则将抛出TooLongFrameException。
lengthFieldOffset:长度字段的偏移量:即对应的长度字段在整个消息数据中得位置
lengthFieldLength:长度字段的长度:如:长度字段是int型表示,那么这个值就是4(long型就是8)
lengthAdjustment:要添加到长度字段值的补偿值
initialBytesToStrip:从解码帧中去除的第一个字节数
*/
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
//自定义协议编码器
pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
//对象参数类型编码器
pipeline.addLast("encoder", new ObjectEncoder());
//对象参数类型解码器
pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
pipeline.addLast("handler",consumerHandler);
}
});

            ChannelFuture future = b.connect("localhost", 8080).sync();  
            future.channel().writeAndFlush(msg).sync();  
            future.channel().closeFuture().sync();  
        } catch(Exception e){  
            e.printStackTrace();  
        }finally {  
            group.shutdownGracefully();  
        }

从上面的客户端代码虽然简单, 但是却展示了 Netty 客户端初始化时所需的所有内容:

1、EventLoopGroup:不论是服务器端还是客户端, 都必须指定 EventLoopGroup。在这个例子中, 指定了

NioEventLoopGroup, 表示一个 NIO 的 EventLoopGroup。

2、ChannelType: 指定 Channel 的类型。 因为是客户端,因此使用了 NioSocketChannel。

3、Handler: 设置处理数据的 Handler。

下面我们继续深入代码,看一下客户端通过 Bootstrap 启动后,都做了哪些工作?我们看一下 NioSocketChannel 的

类层次结构如下:

回到我们在客户端连接代码的初始化 Bootstrap 中调用了一个 channel()方法,传入的参数是 NioSocketChannel.class,

在这个方法中其实就是初始化了一个 ReflectiveChannelFactory 的对象:

public B channel(Class channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory(channelClass));
}

而 ReflectiveChannelFactory 实现了 ChannelFactory 接口, 它提供了唯一的方法, 即 newChannel()方法,

ChannelFactory, 顾名思义, 就是创建 Channel 的工厂类。进入到 ReflectiveChannelFactory 的 newChannel()方法中,

我们看到其实现代码如下: 

public T newChannel() {
// 删除了 try…catch 块
return clazz.newInstance();
}

根据上面代码的提示,我们就可以得出:

1、Bootstrap 中的 ChannelFactory 实现类是 ReflectiveChannelFactory。

2、通过 channel()方法创建的 Channel 具体类型是 NioSocketChannel。

Channel 的实例化过程其实就是调用 ChannelFactory 的 newChannel()方法,而实例化的 Channel 具体类型又是和初

始化 Bootstrap 时传入的 channel()方法的参数相关。因此对于客户端的 Bootstrap 而言,创建的 Channel 实例就是

NioSocketChannel。 

6.1.3 客户端 Channel 的初始化

前面我们已经知道了如何设置一个 Channel 的类型,并且了解到 Channel 是通过 ChannelFactory 的 newChannel()方

法来实例化的, 那么 ChannelFactory 的 newChannel()方法在哪里调用呢?继续跟踪, 我们发现其调用链如下:

在 AbstractBootstrap 的 initAndRegister()中调用了 ChannelFactory()的 newChannel()来创建一个 NioSocketChannel

的实例,其源码如下:

final ChannelFuture initAndRegister() {
Channel channel = null;

    try {  
        channel = this.channelFactory.newChannel();  
        this.init(channel);  
    } catch (Throwable var3) {  
        if (channel != null) {  
            channel.unsafe().closeForcibly();  
            return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);  
        }

        return (new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE)).setFailure(var3);  
    }

    ChannelFuture regFuture = this.config().group().register(channel);  
    if (regFuture.cause() != null) {  
        if (channel.isRegistered()) {  
            channel.close();  
        } else {  
            channel.unsafe().closeForcibly();  
        }  
    }

    return regFuture;  
}

在 newChannel()方法中,利用反射机制调用类对象的 newInstance()方法来创建一个新的 Channel 实例,相当于调用

NioSocketChannel 的默认构造器。NioSocketChannel 的默认构造器代码如下:

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章