场景

在所有我们展示过的代码示例中,我们都在引导的过程中调用了handler()或者childHandler()方法来添加单个的ChannelHandler。

这对于简单的应用程序来说可能已经足够了,但是它不能满足更加复杂的需求。

例如,一个必须要支持多种协议的应用程序将会有很多的ChannelHandler,而不会是一个庞大而又笨重的类。

正如你经常所看到的一样,你可以根据需要,通过在ChannelPipeline 中将它们链接在一起来部署尽可能多的ChannelHandler。

但是,如果在引导的过程中你只能设置一个ChannelHandler,

那么你应该怎么做到这一点呢?

ChannelInboundHandlerAdapter

正是针对于这个用例,Netty 提供了一个特殊的 ChannelInboundHandlerAdapter 子类:

public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter

它定义了下面的方法:

protected abstract void initChannel(C ch) throws Exception;

这个方法提供了一种将多个ChannelHandler 添加到一个ChannelPipeline 中的简便方法。

你只需要简单地向Bootstrap 或ServerBootstrap 的实例提供你的ChannelInitializer 实现即可,并且一旦Channel 被注册到了它的EventLoop 之后,就会调用你的 initChannel()版本。

在该方法返回之后,ChannelInitializer 的实例将会从Channel-Pipeline 中移除它自己。

代码示例

代码清单8-6 定义了ChannelInitializerImpl 类, 并通过ServerBootstrap 的 childHandler()方法注册它。

你可以看到,这个看似复杂的操作实际上是相当简单直接的。

package com.github.houbb.netty.inaction.bootstrap;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @author binbin.hou
 * @date 2019/4/30
 * @since 1.0.0
 */
public class ChannelInit {

    public static void main(String[] args) throws InterruptedException {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        EventLoopGroup eventExecutors = new NioEventLoopGroup();

        serverBootstrap.group(eventExecutors)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<Channel>() {
                    @Override
                    protected void initChannel(Channel ch) throws Exception {
                        ChannelPipeline channelPipeline = ch.pipeline();
                        channelPipeline.addLast(new SimpleChannelInboundHandler<ByteBuf>() {
                            @Override
                            protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
                                System.out.println("hello init~");
                            }
                        });
                    }
                });

        ChannelFuture channelFuture = serverBootstrap.bind("127.0.0.1", 8080)
                .sync();    //同步等待,直到连接完成

        System.out.println("Server start listen at " + 8080);
        channelFuture.channel().closeFuture().sync();
        System.out.println("执行到这里 " + 8080);
    }
}

如果你的应用程序使用了多个 ChannelHandler,请定义你自己的 ChannelInitializer 实现来将它们安装到 ChannelPipeline 中。

收获

  1. 在框架设计的时候要考虑足够的灵活性。保证别人在使用的时候不会感觉这个框架很多地方的设计都不完善。

  2. 灵活源于接口。

参考资料

《Netty in Action》 P128