Netty(六)揭开 BootStrap 的神秘面纱

Posted on 2020-06-17 00:32  FLGB  阅读(185)  评论(0编辑  收藏  举报

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<SocketChannel>() {
                            @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<? extends C> channelClass) { 
if (channelClass == null) { 
    throw new NullPointerException("channelClass"); 
}
return channelFactory(new ReflectiveChannelFactory<C>(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 的默认构造器代码如下: 

Copyright © 2024 FLGB
Powered by .NET 9.0 on Kubernetes