Loading

Netty Bootstrap组件

Bootstrap引导类

一个 Netty 应用通常由一个 Bootstrap 开始,主要作用是配置整个 Netty 程序,串联各个组件,而客户端和服务端这两种应用程序间通用的引导步骤有AbstractBootstrap处理;

 

引导类的层次结构

Netty 中 Bootstrap 类是客户端程序的启动引导类,ServerBootstrap 是服务端启动引导类;

 

为何引导类要实现Cloneable接口?

有时可能会需要创建多个具有类似配置或者完全相同配置的Channel(如链式调用那里添加配置);为了支持这种模式而又不需要为每个Channel都创建并配置一个新的引导类实例,AbstractBootstrap被标记为Cloneable的引导类实例;

注意,这种方式只会创建引导类实例的EventLoopGroup的一个浅拷贝,所以被浅拷贝的EventLoopGroup将在所有克隆的Channel实例之间共享;这是可以接受的,因为通常这些克隆的Channel的生命周期都很短暂;如创建一个Channel以进行一次HTTP请求;

对于AbstractBootstrap的子类型 B 是其父类型的一个类型参数,因此可以返回到运行时实例的引用以支持方法的链式调用(也就是所谓的流式语法

 

父子通道的概念

在Netty中,每一个NioSocketChannel通道所封装的是Java NIO通道,再往下就对应到操作系统底层的socket文件描述符,理论上讲,操作系统底层的socket文件描述符分为两类:

  • 用于接受客户端连接

用于接受客户端连接的socket描述符,处于服务器端,它负责接收客户端的套接字连接;

  • 用于数据传输

用于数据传输的socket描述符,负责传输数据;如同一条TCP的Socket传输链路,在服务端和客户端,它们都分别会有一个与之相对应的数据传输类型的socket文件描述符;

在Netty中,异步非阻塞的服务端监听通道为NioServerSocketChannel,封装的Linux底层的文件描述符是用于接受客户端连接的socket描述符;而异步非阻塞的传输通道NioSocketChannel,封装的是用于数据传输的socket描述符;

在Netty中,将有接收关系的ServerSocketChannel和SocketChannel称为父子通道;其中,NioServerSocketChannel负责服务器连接监听和接收的监听通道,称为父通道(Parent Channel),而NioSocketChannel对应于每一个接收到的传输类通道,称为子通道(Child Channel);

 

EventLoopGroup事件轮询组的概念

Reactor反应器模式的具体实现分为单线程实现版本和多线程实现版本;

多线程版本的Reactor模型如下图

参考:[https://gee.cs.oswego.edu/dl/cpjslides/nio.pdf]

 

在Netty中,一个 EventLoop相当于一个子反应器(SubReactor),一个NioEventLoop子反应器拥有了一个事件轮询线程,同时拥有一个 Java NIO选择器;

Netty中的Reactor反应器模式实现,不是单线程版本的反应器模式,而是多线程版本的反应器模式;Netty多线程版本的反应器模式实现是使用 EventLoopGroup轮询组;多个EventLoop线程放在一起,可以组成一个EventLoopGroup轮询组;EventLoopGroup轮询组就是一个多线程版本的反应器,其中的单个 EventLoop线程对应于一个子反应器(SubReactor);

Netty的程序开发不会直接使用单个 EventLoop事件轮询器,而是使用 EventLoopGroup轮询组;EventLoopGroup的构造函数有一个参数,用于指定内部的线程数;在构造器初始化时,会按照传入的线程数量,在内部构造多个Thread线程和多个EventLoop子反应器(一个线程对应一个EventLoop子反应器),进行多线程的 IO事件查询和分发;

EventLoopGroup的无参数的构造函数,没有传入线程数量或者传入的数量为0,默认的EventLoopGroup内部线程数量为最大可用的CPU处理器数量的2倍,假设电脑使用的是4核的CPU,那么在内部会启动8个EventLoop线程,相当8个子反应器(SubReactor)实例;

 

服务端为了及时接受(Accept)到新连接,在服务端,一般有两个独立的反应器,一个反应器负责新连接的监听和接受,另一个反应器负责 IO事件轮询和分发,两个反应器相互隔离,对应到Netty服务器程序中,则需要设置两个EventLoopGroup轮询组,一个组负责新连接的监听和接受,另外一个组负责 IO传输事件 的轮询与分发,两个轮询组的职责具体如下:

  • 负责新连接的监听和接收的EventLoopGroup轮询组中的反应器(Reactor),完成查询通道的新连接I/O事件查询;
  • 另一个轮询组中的反应器(Reactor),完成查询所有子通道的I/O事件,并且执行对应的Handler处理器完成I/O处理(如:数据的输入和输出);

 

Bootstrap启动流程步骤

Bootstrap的启动流程,即Netty组件的组装、配置,以及 Netty服务器或者客户端的启动流程;

  • 首先创建一个服务器端的引导类实例;
ServerBootstrap b = new ServerBootstrap();

 

  • 创建反应器轮询组,并设置到ServerBootstrap引导类实例;
//创建reactor 线程组
EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1);
EventLoopGroup workerLoopGroup = new NioEventLoopGroup();

//设置reactor 线程组
b.group(bossLoopGroup, workerLoopGroup);

在设置反应器轮询组之前,创建了两个 NioEventLoopGroup轮询组,一个负责处理连接监听I/O事件为bossLoopGroup;另一个负责数据传输事件和处理为workerLoopGroup;在两个轮询组创建完成后,就可以配置给引导类实例,它一次性地给引导类配置了两大轮询组;

 

注:如果不需要进行新连接事件和输出事件进行分开监听,就不一定非得配置两个轮询组,可以仅配置一个EventLoopGroup反应器轮询组,具体的配置如下:

b.group(workerGroup);

在这种模式下,新连接监听I/O事件和数据传输I/O事件可能被挤在了同一个线程中处理;这样会带来一个风险:新连接的接受被更加耗时的数据传输或者业务处理所阻塞;因此,在服务器端建议设置成两个轮询组的工作模式;

 

  • 设置通道的I/O类型,Netty不止支持 Java NIO,也支持阻塞式的OIO(OIO阻塞式I/O,如果Bootstrap的I/O模型需要指定为BIO类型,则可以配置为OioServerSocketChannel类即可);
//设置nio类型的channel
b.channel(NioServerSocketChannel.class);

 

  • 设置监听端口;
//设置监听端口
b.localAddress(serverPort);

这一操作主要是设置服务器的监听地址;

 

  • 设置传输通道的配置选项;
//设置通道的参数
b.option(ChannelOption.SO_KEEPALIVE, true);
b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
b.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
b.childOption(ChannelOption.TCP_NODELAY, true);

Bootstrap的option选项设置方法,对于服务端来说,这个方法是用于给父通道(Parent Channel)设置一些与传输协议相关的选项;如果要给子通道(Child Channel)设置一些通道选项,则需要用另一个childOption的设置方法;

 

  • 装配子通道的Pipeline流水线;

每一个通道都用一条ChannelPipeline流水线,它的内部有一个双向的链表;装配流水线的方式:将业务处理器ChannelHandler实例包装之后加入双向链表中;

装配子通道的Handler流水线调用引导类的childHandler方法,该方法需要传入一个ChannelInitializer通道初始化类的实例作为参数;每当父通道成功接收一个连接,并创建成功一个子通道后,就会初始化子通道,此时这里配置的ChannelInitializer实例就会被调用;

在ChannelInitializer通道初始化类的实例中,有一个initChannel初始化方法,在子通道创建后会被执行到,向子通道流水线增加业务处理器;

//装配子通道流水线
b.childHandler(new ChannelInitializer<SocketChannel>() {
    //有连接到达时会创建一个channel
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        // pipeline管理子通道channel中的Handler
        ch.pipeline().addLast(new NettyDiscardHandler());
    }
});

 

为何仅装配子通道的流水线,而不需要装配父通道的流水线?

父通道NioServerSocketChannel的内部业务处理是固定的:即接受新连接后,创建子通道,然后初始化子通道,因此不需要特别的配置,由Netty自行进行装配;如果需要完成特殊的父通道业务处理,可以类似地使用ServerBootstrap的handler方法,为父通道设置ChannelInitializer初始化器;

 

注:在装配流水线时需要注意ChannelInitializer处理器有一个泛型参数SocketChannel,它代表需要初始化的通道类型,这个类型需要和引导类中设置的传输通道类型,保持一一对应起来;

  • 开始绑定服务器新连接的监听端口;
//开始绑定端口
ChannelFuture channelFuture = b.bind();

channelFuture.addListener(l -> {
    if (l.isSuccess()) {
        logger.info(" 服务器端口绑定 正常: {}", channelFuture.channel().localAddress());
    } else {
        logger.info(" 服务器端口绑定 失败: {}", channelFuture.channel().localAddress());
    }
});

//通过调用sync同步方法阻塞直到绑定成功
channelFuture.sync();
logger.info(" 服务器启动成功,监听端口: ", channelFuture.channel().localAddress());

Bootstrap#bind方法的作用:返回一个端口绑定 Netty的异步任务ChannelFuture(ChannelFuture可以给异步任务增加回调监听器),阻塞channelFuture异步任务,直到端口绑定任务执行完成;

Netty中所有的I/O操作都是异步执行的,这就意味着任何一个I/O操作都会立刻返回,但在返回的时候,异步任务还没有真正执行,如下图;

通过该异步任务实例,既可以实现同步阻塞一直到 ChannelFuture异步任务执行完成,也可以为其增加事件监听器的方式注册异步回调逻辑,以获得Netty中的I/O操作的真正结果;

 

  • 使用ChannelFuture阻塞,直到监听通道关闭;
//等待通道关闭的异步任务结束
//服务监听通道会一直等待通道关闭的异步任务结束
ChannelFuture closeFuture = channelFuture.channel().closeFuture();
closeFuture.sync();

阻塞当前线程直到通道关闭,可以使用通道的closeFuture方法,以获取通道关闭的异步任务;当通道被关闭时,closeFuture实例的sync方法会返回;

 

  • 关闭EventLoopGroup;
//优雅关闭EventLoopGroup,释放掉所有资源包括创建的线程
workerLoopGroup.shutdownGracefully();
bossLoopGroup.shutdownGracefully();

关闭Reactor反应器轮询组,同时会关闭内部的SubReactor子反应器线程,内部的Selector选择器、内部的轮询线程以及负责查询的所有的子通道;在子通道关闭后,会释放掉底层的资源,如Socket文件描述符等;

 

ChannelOption通道选项

无论是NioServerSocketChannel父通道类型,还是NioSocketChannel子通道类型,都可以设置一系列的ChannelOption通道选项,ChannelOption包括了底层连接的详细信息;常见的选项如下:

  • SO_SNDBUF和SO_RCVBUF

此为TCP传输选项,每个TCP Socket(套接字)在内核中都有一个发送缓冲区(SO_SNDBUF)和一个接收缓冲区(SO_RCVBUF),这两个选项就是用来设置 TCP连接的这两个缓冲区大小的;TCP的全双工工作模式以及 TCP的滑动窗口对两个独立的缓冲区都有依赖;

 

  • TCP_NODELAY

TCP_NODELAY的值,设置为true表示关闭延迟,设置为false表示开启延迟,其值与是否开启 Nagle 算法是相反的;在Netty中,TCP_NODELAY的值默认为true,而操作系统默认为false;

如果要求高实时性,有数据发送时就马上发送,就将该选项设置为 true(关闭 Nagle算法);如果要减少发送次数减少网络交互,就设置为 false(启用 Nagle算法),等累积一定大小的数据后再发送;

 

  • SO_KEEPALIVE

SO_KEEPALIVE为TCP传输选项,表示是否开启 TCP协议的心跳机制,true为连接保持心跳,默认值为false;

启用该功能时,TCP会主动探测空闲连接的有效性,可以将此功能视为 TCP的心跳机制;

注:默认的心跳间隔是7200秒;在Netty中默认关闭该功能;

 

  • SO_REUSEADDR

此为TCP传输选项,如果为 true时表示地址复用,默认值为 false;有四种情况需要用到这个参数设置:

  1. 当有一个地址和端口相同的连接socket1处于TIME_WAIT状态时,而又希望启动一个新的连接socket2要占用该地址和端口;
  2. 有多块网卡或用IP Aliasinghttps://en.wikipedia.org/wiki/IP_aliasing)的机器在同一端口启动多个进程,但每个进程绑定的本地 IP地址不能相同;
  3. 同一进程绑定相同的端口到多个Socket(套接字),但每个Socket绑定的IP地址不同;
  4. 完全相同的地址和端口的重复绑定,但这只能用于UDP,不用于TCP;

 

  • SO_LINGER

此为TCP传输选项,选项可以用来控制socket.close()方法被调用后的行为,包括延迟关闭时间;

如果此选项设置为-1,表示socket.close()方法在调用后立即返回,但操作系统底层会将发送缓冲区的数据全部发送到对端(SO_LINGER的默认值为 -1,表示禁用该功能);

如果此选项设置为0,表示socket.close()方法在调用后会立即返回,但是操作系统会放弃发送缓冲区数据,而是直接向对端发送 RST包,对端将收到复位错误;

如果此选项设置为非0正整数值,表示调用socket.close()方法的线程被阻塞,直到延迟时间到来,发送缓冲区中的数据发送完毕,若超时,则对端会收到复位错误;

 

  • SO_BACKLOG

此为TCP传输选项,表示服务器端接收连接的队列长度,如果队列已满,客户端连接将被拒绝;

服务端在处理客户端新连接请求时(三次握手)是顺序处理的,同一时间只能处理一个客户端连接,多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,队列的大小通过 SO_BACKLOG指定;

 

  • SO_BROADCAST

此为TCP传输选项,表示设置为广播模式;

 

在某些常用的属性和数据不可用时,Netty提供了AttributeMap抽象接口(一个由Channel 和引导类提供的集合)以及AttributeKey(一个用于插入和获取属性值的泛型类);

io.netty.util.AttributeMap

io.netty.util.AttributeKey

使用这些Netty内置工具便可以安全地将任何类型的数据项与客户端和服务端Channel(包含ServerChannel 的子通道)相关联;

//创建一个AttributeKey以标识该属性
final AttributeKey<Integer> id = new AttributeKey<Integer>("ID");
//存储该id属性
b.attr(id,123456);

 

引导客户端

Bootstrap 类负责为客户端和使用无连接协议的应用程序创建Channel;

Bootstrap类的API

名称 描述
Bootstrap group(EventLoopGroup group) 设置用于处理Channel所有事件的EventLoopGroup;
Bootstrap channel(Class<? extends C> channelClass)
Bootstrap channelFactory(ChannelFactory<? extends C> channelFactory)
channel方法指定了Channel的实现类;如果该实现类没提供默认的构造函数,可以通过调用channelFactory方法来指定一个工厂类,它将会被bind方法调用;
Bootstrap localAddress(SocketAddress localAddress) 指定Channel应该绑定到的本地地址;如果没有指定,则将由操作系统创建一个随机的地址,或者可以通过bind或者connect方法指定localAddress;
<T> Bootstrap option(ChannelOption<T> option, T value)

设置ChannelOption,其将被应用到每个新创建的Channel的ChannelConfig;

这些选项将会通过bind或者connect方法设置到Channel,不管哪个先被调用;

这个方法在Channel已经被创建后再调用将不会有任何的效果;

支持的ChannelOption取决于使用的Channel类型;

<T> Bootstrap attr(Attribute<T> key, T value) 指定新创建的Channel 的属性值。这些属性值是通过bind或者connect方法设置到Channel 的,具体取决于谁最先被调用;这个方法在Channel被创建后将不会有任何的效果;
Bootstrap handler(ChannelHandler handler) 设置将被添加到ChannelPipeline以接收事件通知的ChannelHandler;
Bootstrap clone() 创建一个当前Bootstrap的克隆,其具有和原始的Bootstrap 相同的设置信息;
Bootstrap remoteAddress(SocketAddress remoteAddress) 设置远程地址,或者可以通过connect方法来指定它;
ChannelFuture connect() 连接到远程节点并返回一个ChannelFuture,其将会在连接操作完成后接收到通知;
ChannelFuture bind() 绑定Channel并返回一个ChannelFuture,其将会在绑定操作完成后接收到通知,在那之后必须调用Channel#connect方法来建立连接;

 

使用如下:

查看代码
public class NettyClient {
    public static void main(String[] args) {
        final int port =  19000 ;
        final String local =  "127.0.0.1" ;
        EventLoopGroup group =  new NioEventLoopGroup();

        try {
            // 创建一个BootStrap类的实例以创建和连接新的Channel
            Bootstrap bootstrap =  new Bootstrap();

            // 链式写法
            // 设置EventLoopGroup,提供用于处理Channel事件的EventLoop
            bootstrap.group(group)
                    // 指定要使用的Channel实现
                    .channel(NioSocketChannel. class )
                    // 设置用于用于处理Channel事件的Handler
                    .handler( new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch)  throws Exception {
                            ch.pipeline().addLast( new NettyClientHandler());
                        }
                    });

            // 连接服务端
            ChannelFuture channelFuture = bootstrap.connect(local, port).sync();
            channelFuture.channel().closeFuture().sync();
        }  catch (Exception e) {
            e.printStackTrace();
        }  finally {
            group.shutdownGracefully();
        }
    }
}

除了 connect方法以外, BootStrap的其他方法将通过每次方法调用所返回Bootstrap实例的引用;

在引导的过程中,在调用bind或者connect方法之前,必须调用以下方法来设置所需的组件(如果不这样做, 则将会导致 IllegalStateException):

  • group方法;
  • channel或者channelFactory方法;
  • handler方法需要通过该方法配置ChannelPipeline;

 

引导服务器

ServerBootstrap 是服务端启动引导类;

ServerBootstrap在bind方法被调用时创建了一个ServerChannel,并且该 ServerChannel 管理了多个子 Channel;

ServerChannel的实现负责创建子 Channel,这些子Channel 代表了已被接受的连接(已连接的客户端)

负责引导 ServerChannel 的 ServerBootstrap 提供了的方法,以简化将设置应用到已被接受的子Channel的ChannelConfig 的任务;

 

ServerBootstrap类的API

名称 描述

ServerBootstrap group(EventLoopGroup group)

ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup)

设置ServerBootstrap要用的EventLoopGroup;这个EventLoopGroup将用于ServerChannel和被接受的子Channel的I/O处理;
ServerBootstrap channel(Class<? extends C> channelClass) 设置将要被实例化的ServerChannel类;
ServerBootstrap channelFactory(ChannelFactory<? extends C> channelFactory) 如果不能通过默认的构造函数创建Channel,那么可以提供一个ChannelFactory;
ServerBootstrap localAddress(SocketAddress localAddress)

指定ServerChannel应该绑定到的本地地址;

如果没有指定,则将由操作系统使用一个随机地址,或者可以通过bind方法来指定该localAddress;

<T> ServerBootstrap option(ChannelOption<T> option, T value)

指定要应用到新创建的ServerChannel 的ChannelConfig的ChannelOption;

这些选项将会通过bind方法设置到Channel,在bind方法被调用之后,设置或者改变ChannelOption都不会有任何的效果;所支持的ChannelOption 取决于所使用的Channel 类型;

<T> ServerBootstrap childOption(ChannelOption<T> option, T value) 指定当子Channel被接受时,应用到子Channel的ChannelConfig的ChannelOption;所支持的ChannelOption 取决于所使用的Channel的类型;

<T> ServerBootstrap  attr(AttributeKey<T> key, T value)

指定ServerChannel上的属性,属性将会通过bind方法设置给Channel;在调用bind方法之后改变它们将不会有任何的效果;

<T> ServerBootstrap childAttr(AttributeKey<T> childKey, T value)

将属性设置给已经被接受的子Channel,接下来的调用将不会有任何的效果;
ServerBootstrap handler(ChannelHandler handler) 设置被添加到ServerChannel 的ChannelPipeline 中的ChannelHandler
ServerBootstrap childHandler(ChannelHandler childHandler)

设置将被添加到已被接受的子Channel 的ChannelPipeline中的ChannelHandler;

handler方法和childHandler方法之间的区别:前者所添加的ChannelHandler由接受子Channel 的ServerChannel 处理,而childHandler方法所添加的ChannelHandler 将由已被接受的子Channel处理,其代表一个绑定到远程节点的套接字;

ServerBootstrap clone() 克隆一个设置和原始的ServerBootstrap相同的ServerBootstrap;
ChannelFuture bind() 绑定ServerChannel并且返回一个ChannelFuture,其将会在绑定操作完成后收到通知(带着成功或者失败的结果);

 

使用如下:

查看代码
public class NettyServer {

    private final static Logger logger = LoggerFactory.getLogger(NettyServer. class );

    public static void main(String[] args) {
        // 创建两个线程组bossGroup和workerGroup,含有子线程NioEventLoop的个数默认为cpu核数的两倍
        // bossGroup只处理连接请求,业务处理交由wokerGroup
        EventLoopGroup bossGroup =  new NioEventLoopGroup( 1 );
        EventLoopGroup workerGroup =  new NioEventLoopGroup();
        final int port =  19000 ;

        try {
            // 创建服务的启动对象
            ServerBootstrap bootstrap =  new ServerBootstrap();

            // 设置两个线程组
            bootstrap.group(bossGroup, workerGroup)
                    // 指定使用NioServerSocketChannel作为服务器的通道实现
                    .channel(NioServerSocketChannel. class )
                    // 初始化服务器连接队列大小,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接
                    // 多个客户端同时来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理
                    .option(ChannelOption.SO_BACKLOG,  1024 )
                    .childHandler( new ChannelInitializer<SocketChannel>() {
                        //创建通道初始化对象,设置初始化参数
                        @Override
                        protected void initChannel(SocketChannel ch)  throws Exception {
                            //对workerGroup的SocketChannel设置处理器codec
                            ch.pipeline().addLast( new NettyServerHandler());
                        }
                    });

            logger.info( "netty server start..." );

            // 通过配置好ServerBootStrap的实例绑定该Channel
            // 启动服务器(并绑定端口),bind是异步操作,sync方法是等待异步操作执行完毕,这里会阻塞住
            ChannelFuture channelFuture = bootstrap.bind(port).sync();

            // 添加监听器
            channelFuture.addListener( new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future)  throws Exception {
                    if (future.isSuccess()) {
                        logger.info( "监听端口" + port +  "成功" );
                    }  else {
                        logger.info( "监听失败..." );
                    }
                }
            });

            channelFuture.channel().closeFuture().sync();
        }  catch (Exception e) {
            logger.error( "异常.." , e);
        }  finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

 

从Channel引导客户端

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

如果创建新的BootStrap实例,并且每个新创建的客户端Channel定义另一个EventLoop,这会产生额外的线程,以及在已被接受的子 Channel 和客户端 Channel 之间交换数据时不可避免的上下文切换;


通过将已被接受的子通道(已连接的客户端) EventLoop传递给Bootstrap的group方法来共享该EventLoop;因为分配给EventLoop的所有Channel都使用同一个线程,所以这避免了额外的线程创建和Channel之间数据交换时的上下文切换;如下图;

子通道handler中共享EventLoop,在子通道handler的写法如下:

// 创建一个Bootstrap类的类型以连接到远程
Bootstrap bootstrap = new Bootstrap();
// 使用与分配给已被接受的子Channel相同的EventLoop
bootstrap.group(ctx.channel().eventLoop());

 

在引导过程中添加多个ChannelHandler

在引导的过程中调用了handler或者childHandler方法来添加单个的ChannelHandler,这对于简单的应用程序来说可能已经足够了,但是它不能满足更加复杂的需求,如一个必须要支持多种协议的应用程序将会有很多的ChannelHandler,而不会是一个庞大而又笨重的类;

开发人员可以根据需要,通过在ChannelPipeline 中将它们链接在一起来部署尽可能多的ChannelHandler;

如果在引导的过程中开发人员只能设置一个ChannelHandler,那应该如何处理?

Netty提供了了一个特殊的ChannelInboundHandlerAdapter子类ChannelInitializer,使用ChannelInitializer可向流水线中装配业务处理器;

 

io.netty.channel.ChannelInitializer

ChannelInitializer中initChannel方法是一个抽象方法,需要开发人员自己实现;

这个方法可用于一种将多个ChannelHandler 添加到一个ChannelPipeline 中的简便方法;

开发人员只需要简单地向Bootstrap 或ServerBootstrap 的实例提供你的ChannelInitializer实现即可,并且一旦Channel被注册到了它的EventLoop之后,就会调用initChannel版本;在该方法返回之后,ChannelInitializer的实例将会从ChannelPipeline中移除它自己;

 

使用例子如下:

b.childHandler(new ChannelInitializer<SocketChannel>() {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        // pipeline管理子通道channel中的Handler
        ch.pipeline().addLast(new NettyDiscardHandler());
    }
});
posted @ 2021-02-21 15:50  街头卖艺的肖邦  阅读(247)  评论(0编辑  收藏  举报