Netty4.1源码分析之服务启动

一、服务启动官方example

// Echoes back any received data from a client.
 */
public final class EchoServer {

    static final boolean SSL = System.getProperty("ssl") != null;
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static final class Callabler implements Callable {

        @Override
        public Object call() throws Exception {
            return null;
        }
    }

    public static void main(String[] args) throws Exception {
        // Configure SSL.
        // 配置 SSL
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }

        // Configure the server.
        // 创建两个 EventLoopGroup 对象
        EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 创建 boss 线程组 用于服务端接受客户端的连接
        EventLoopGroup workerGroup = new NioEventLoopGroup(); // 创建 worker 线程组 用于进行 SocketChannel 的数据读写

//        ((NioEventLoop) workerGroup.next()).threadProperties();
//        Collection<Callabler> callablers = new ArrayList<Callabler>();
//        for (int i = 0; i < 10; i++) {
//            callablers.add(new Callabler());
//        }

//        Set<Callable<Boolean>> set = Collections.<Callable<Boolean>>singleton(new Callable<Boolean>() {
//            @Override
//            public Boolean call() throws Exception {
//                return Boolean.TRUE;
//            }
//        });
//        ((NioEventLoop) workerGroup.next()).invokeAny(set);

//        Thread.sleep(Long.MAX_VALUE);

        // 创建 EchoServerHandler 对象
        final EchoServerHandler serverHandler = new EchoServerHandler();
        try {
            // 创建 ServerBootstrap 对象
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup) // 设置使用的 EventLoopGroup
             .channel(NioServerSocketChannel.class) // 设置要被实例化的为 NioServerSocketChannel 类
             .option(ChannelOption.SO_BACKLOG, 100) // 设置 NioServerSocketChannel 的可选项
             .handler(new LoggingHandler(LogLevel.INFO)) // 设置 NioServerSocketChannel 的处理器
//                .handler(new IdleStateHandler(0, 1, 0, TimeUnit.SECONDS))
//            .handler(new ChannelInitializer<Channel>() {
//
//                @Override
//                protected void initChannel(Channel ch) {
//                    final ChannelPipeline pipeline = ch.pipeline();
                    ch.eventLoop().execute(new Runnable() {
                        @Override
                        public void run() {
                            pipeline.addLast(new LoggingHandler(LogLevel.INFO));
                        }
                    });
//                    pipeline.addLast(new LoggingHandler(LogLevel.INFO));
                    pipeline.addLast(new ChannelOutboundHandlerAdapter() {

                        @Override
                        public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
//                            super.bind(ctx, localAddress, promise);
                            if (true) {
                                throw new RuntimeException("测试异常");
                            }

                        }

                        @Override
                        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                            super.exceptionCaught(ctx, cause);
                        }
                    });
//
                    pipeline.addLast(new ChannelInboundHandlerAdapter() {

//                        @Override
//                        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//                            super.exceptionCaught(ctx, cause);
//                        }

                        @Override
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                            throw new RuntimeException("测试异常");
                        }
                    });
//                }
//
//            })
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception { // 设置连入服务端的 Client 的 SocketChannel 的处理器
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc()));
                     }

//                     p.addLast(new IdleStateHandler(0, 1, 0, TimeUnit.SECONDS));

                     p.addLast(new LineBasedFrameDecoder(Integer.MAX_VALUE));

                     //p.addLast(new LoggingHandler(LogLevel.INFO));
                     p.addLast(serverHandler);
                 }
             })

            .childOption(ChannelOption.SO_SNDBUF, 5)
            .childOption(ChannelOption.SO_LINGER, 100)

            ;

            // Start the server.
            // 绑定端口,并同步等待成功,即启动服务端
            ChannelFuture f = b.bind(PORT).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    System.out.println("测试下被触发");
                }
            }).sync();

//            bossGroup.schedule(new Runnable() {
//                @Override
//                public void run() {
//                    System.out.println("执行定时任务");
//                }
//            }, 5, TimeUnit.SECONDS);

//            f.channel().writeAndFlush("123").addListener(new ChannelFutureListener() {
//                @Override
//                public void operationComplete(ChannelFuture future) throws Exception {
//                    System.out.println("干啥呢");
//                }
//            });

//            f.channel().close();

            // Wait until the server socket is closed.
            // 监听服务端关闭,并阻塞等待
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            // 优雅关闭两个 EventLoopGroup 对象
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

这里我们主要分析以下代码

// 绑定端口,并同步等待成功,即启动服务端
            ChannelFuture f = b.bind(PORT).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    System.out.println("测试下被触发");
                }
            }).sync();

跟踪bind方法调用代码

    /**
     * Create a new {@link Channel} and bind it.
     */
    public ChannelFuture bind(int inetPort) {
        return bind(new InetSocketAddress(inetPort));
    }

继续跟踪bing调用

 /**
     * Create a new {@link Channel} and bind it.
     */
    public ChannelFuture bind(SocketAddress localAddress) {
        // 校验服务启动需要的必要参数
        validate();
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        // 绑定本地地址( 包括端口 )
        return doBind(localAddress);
    }

然后看doBind方法调用

 private ChannelFuture doBind(final SocketAddress localAddress) {
        // 初始化并注册一个 Channel 对象,因为注册是异步的过程,所以返回一个 ChannelFuture 对象。
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) { // 若发生异常,直接进行返回。
            return regFuture;
        }

        // 绑定 Channel 的端口,并注册 Channel 到 SelectionKey 中。
        if (regFuture.isDone()) { // 未
            // At this point we know that the registration was complete and successful.
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise); // 绑定
            return promise;
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    System.out.println(Thread.currentThread() + ": PendingRegistrationPromise");
                    Throwable cause = future.cause();
                    if (cause != null) {
                        // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
                        // IllegalStateException once we try to access the EventLoop of the Channel.
                        promise.setFailure(cause);
                    } else {
                        // Registration was successful, so set the correct executor to use.
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();

                        doBind0(regFuture, channel, localAddress, promise); // 绑定
                    }
                }
            });
            return promise;
        }
    }

我们主要关注以下两个方法调用

// 初始化并注册一个 Channel 对象,因为注册是异步的过程,所以返回一个 ChannelFuture 对象。
        final ChannelFuture regFuture = initAndRegister();
        。。。。
          doBind0(regFuture, channel, localAddress, promise); // 绑定

其实,从方法名上面我们已经可以略窥一二,init->初始化,register->注册,那么到底要注册到什么呢?联系到nio里面轮询器的注册,可能是把某个东西初始化好了之后注册到selector上面去,最后bind,像是在本地绑定端口号,带着这些猜测,

 final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            // 创建 Channel 对象
            channel = channelFactory.newChannel();
            // 初始化 Channel 配置
            init(channel);
        } catch (Throwable t) {
            if (channel != null) { // 已创建 Channel 对象
                // channel can be null if newChannel crashed (eg SocketException("too many open files"))
                channel.unsafe().closeForcibly(); // 强制关闭 Channel
                // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
                return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
            }
            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
            return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
        }

        // 注册 Channel 到 EventLoopGroup 中
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly(); // 强制关闭 Channel
            }
        }

       //如果我们在这里,并且承诺没有失败,则会出现以下情况之一:
        //1)如果我们尝试从事件循环中注册,则此时注册已完成。//也就是说,现在可以安全地尝试bind()或connect(),因为通道已经注册。
        //2)如果我们尝试从另一个线程注册,则注册请求已成功//添加到事件循环的任务队列中,以便以后执行。//也就是说,现在尝试bind()或connect()是安全的:
        //因为bind()或connect()将在*计划的注册任务执行*之后执行//因为register()、bind()和connect()都绑定到同一个线程。

        return regFuture;
    }
(ps:此段分析来自闪电侠https://www.jianshu.com/p/c5068caab217)

我们看到 initAndRegister() 做了几件事情
1.new一个channel
2.init这个channel
3.将这个channel register到某个对象
1.new一个channel
我们首先要搞懂channel的定义,netty官方对channel的描述如下

A nexus to a network socket or a component which is capable of I/O operations such as read, write, connect, and bind

这里的channel,由于是在服务启动的时候创建,我们可以和普通Socket编程中的ServerSocket对应上,表示服务端绑定的时候经过的一条流水线

我们发现这条channel是通过一个 channelFactory new出来的,channelFactory 的接口很简单

public interface ChannelFactory<T extends Channel> extends io.netty.bootstrap.ChannelFactory<T> {
    /**
     * Creates a new channel.
     */
    @Override
    T newChannel();
}

就一个方法,我们查看channelFactory被赋值的地方

AbstractBootstrap.java

public B channelFactory(ChannelFactory<? extends C> channelFactory) {
    if (channelFactory == null) {
        throw new NullPointerException("channelFactory");
    }
    if (this.channelFactory != null) {
        throw new IllegalStateException("channelFactory set already");
    }

    this.channelFactory = channelFactory;
    return (B) this;
}

在这里被赋值,我们层层回溯,查看该函数被调用的地方,发现最终是在这个函数中,ChannelFactory被new出

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

这里,我们的demo程序调用channel(channelClass)方法的时候,将channelClass作为ReflectiveChannelFactory的构造函数创建出一个ReflectiveChannelFactory

demo端的代码如下:

.channel(NioServerSocketChannel.class);

然后回到本节最开始

channelFactory.newChannel();

我们就可以推断出,最终是调用到 ReflectiveChannelFactory.newChannel() 方法,跟进

public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {

    private final Class<? extends T> clazz;

    public ReflectiveChannelFactory(Class<? extends T> clazz) {
        if (clazz == null) {
            throw new NullPointerException("clazz");
        }
        this.clazz = clazz;
    }

    @Override
    public T newChannel() {
        try {
            return clazz.newInstance();
        } catch (Throwable t) {
            throw new ChannelException("Unable to create Channel from class " + clazz, t);
        }
    }
}

看到clazz.newInstance();,我们明白了,原来是通过反射的方式来创建一个对象,而这个class就是我们在ServerBootstrap中传入的NioServerSocketChannel.class

结果,绕了一圈,最终创建channel相当于调用默认构造函数new出一个 NioServerSocketChannel对象
接下来我们就可以将重心放到 NioServerSocketChannel的默认构造函数

private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
public NioServerSocketChannel() {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

private static ServerSocketChannel newSocket(SelectorProvider provider) {
    //...
    return provider.openServerSocketChannel();
}

通过SelectorProvider.openServerSocketChannel()创建一条server端channel,然后进入到以下方法

public NioServerSocketChannel(ServerSocketChannel channel) {
    super(null, channel, SelectionKey.OP_ACCEPT);
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}

这里第一行代码就跑到父类里面去了,第二行,new出来一个 NioServerSocketChannelConfig,其顶层接口为 ChannelConfig,netty官方的描述如下

A set of configuration properties of a Channel.

基本可以判定,ChannelConfig 也是netty里面的一大核心模块,初次看源码,看到这里,我们大可不必深挖这个对象,而是在用到的时候再回来深究,只要记住,这个对象在创建NioServerSocketChannel对象的时候被创建即可

我们继续追踪到 NioServerSocketChannel 的父类

AbstractNioMessageChannel.java

protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent, ch, readInterestOp);
}

继续往上追

AbstractNioChannel.java

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent);
    this.ch = ch;
    this.readInterestOp = readInterestOp;
    //...
    ch.configureBlocking(false);
    //...
}

这里,简单地将前面 provider.openServerSocketChannel(); 创建出来的 ServerSocketChannel 保存到成员变量,然后调用ch.configureBlocking(false);设置该channel为非阻塞模式,标准的jdk nio编程的玩法

这里的 readInterestOp 即前面层层传入的 SelectionKey.OP_ACCEPT,接下来重点分析 super(parent);(这里的parent其实是null,由前面写死传入)

AbstractChannel.java

protected AbstractChannel(Channel parent) {
    this.parent = parent;
    id = newId();
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}

到了这里,又new出来三大组件,赋值到成员变量,分别为

id = newId();
protected ChannelId newId() {
    return DefaultChannelId.newInstance();
}

id是netty中每条channel的唯一标识,这里不细展开,接着

unsafe = newUnsafe();
protected abstract AbstractUnsafe newUnsafe();

查看Unsafe的定义

Unsafe operations that should never be called from user-code. These methods are only provided to implement the actual transport, and must be invoked from an I/O thread

成功捕捉netty的又一大组件,我们可以先不用管TA是干嘛的,只需要知道这里的 newUnsafe方法最终属于类NioServerSocketChannel中

最后

pipeline = newChannelPipeline();

protected DefaultChannelPipeline newChannelPipeline() {
    return new DefaultChannelPipeline(this);
}

protected DefaultChannelPipeline(Channel channel) {
        this.channel = ObjectUtil.checkNotNull(channel, "channel");
        succeededFuture = new SucceededChannelFuture(channel, null);
        voidPromise =  new VoidChannelPromise(channel, true);

        tail = new TailContext(this);
        head = new HeadContext(this);

        head.next = tail;
        tail.prev = head;
}

初次看这段代码,可能并不知道 DefaultChannelPipeline 是干嘛用的,我们仍然使用上面的方式,查看顶层接口ChannelPipeline的定义

A list of ChannelHandlers which handles or intercepts inbound events and outbound operations of a Channel

从该类的文档中可以看出,该接口基本上又是netty的一大核心模块

到了这里,我们总算把一个服务端channel创建完毕了,将这些细节串起来的时候,我们顺带提取出netty的几大基本组件,先总结如下

Channel
ChannelConfig
ChannelId
Unsafe
Pipeline
ChannelHander

初次看代码的时候,我们的目标是跟到服务器启动的那一行代码,我们先把以上这几个组件记下来,等代码跟完,我们就可以自顶向下,逐层分析,我会放到后面源码系列中去深入到每个组件

总结一下,用户调用方法 Bootstrap.bind(port) 第一步就是通过反射的方式new一个NioServerSocketChannel对象,并且在new的过程中创建了一系列的核心组件,仅此而已,并无他,真正的启动我们还需要继续跟

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值