从零开始学Netty(三)浅谈netty引导类ServerBootstrap各个方法的作用

netty官方用户指南

这是netty官方的用户指南,请各位读者先阅读并按照上述文档操作一遍。

浅谈ServerBootstrap启动流程

本次学习的netty环境版本是4.1.45。
按照文档,我们会写丢弃服务器(Discard Server),应答服务器(Echo Server)和时间服务器(Time Serve)。
我们会发现他们都用到了ServerBootstrap这个类,这个类就是整合各种资源,建造服务器的类,他们创建的逻辑基本相似,但各自的handler会有所其别,这是服务器功能不同的根本原因。

以DiscardServer为例子。

public class DiscardServer {
    
    private int port;
    
    public DiscardServer(int port) {
        this.port = port;
    }
    
    public void run() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // 这个是Acceptor的线程组
        EventLoopGroup workerGroup = new NioEventLoopGroup(); // 这个是用来处理已接收连接的线程组
        try {
            ServerBootstrap b = new ServerBootstrap(); // (2)
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class) // (3)指定channel的类型
             .childHandler(new ChannelInitializer<SocketChannel>() { // (4)指定每个worker channel需要进行哪些处理
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ch.pipeline().addLast(new DiscardServerHandler());
                 }
             })
             .option(ChannelOption.SO_BACKLOG, 128)          // (5)指定option参数,SO_BACKLOG指服务端接受连接的队列长度,如果队列已满,客户端连接将被拒绝
             .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)连接保活
    
            // Bind and start to accept incoming connections.
            ChannelFuture f = b.bind(port).sync(); // (7)绑定服务器端口,启动从这开始
    
            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
    
    public static void main(String[] args) throws Exception {
        int port = 8080;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        }

        new DiscardServer(port).run();
    }
}

上面在第7步之前的操作,全部是在配置服务器的行为,指定好服务器的接收连接的线程组,处理已连接channel的线程组,channel的类型,option参数,各个channel要经过怎样的handler。最后在bind函数这正式开始启动服务器。

 //io.netty.bootstrap.ServerBootstrap#group	
  public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        super.group(parentGroup);
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
        return this;
    }
    //io.netty.bootstrap.AbstractBootstrap#group
  public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        super.group(parentGroup);
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
        return this;
    }

以group为例,只是把两个group赋值到的引导类中,等待启动时使用。

bind 才是核心代码,io.netty.bootstrap.AbstractBootstrap#doBind,是干活的方法,其中initAndRegister()中初始化了bossgroup。后面绑定了端口,启动了服务器。因为只是想告诉大家netty引导类中使用的各个方法有啥作用,所以就不在往下细扣源码了。

private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }

        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 {
                    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;
        }
    }

联系网络编程,理解各个方法的作用

我们来对照网络交互的几个关键点来看,
1.首先是建立连接(这个由bossGroup 来完成,其内部实现还是用到了select,当连接进来后会把进来的channel注册给workerGroup 的select,之后的读写由workerGroup 完成)

2.然后是服务器根据连接的信息进行业务处理,包括返回数据(这步有对应上面的childHandler的pipeline的初始化操作,即由客户端发来的消息会被封装成channel,然后经过这个pipeline中各种handler的处理,首先因为网络传输是字节流的形式,所以要先进行解码,变成我们能处理的字符,然后根据这些字符进行业务处理,业务处理也是其中的一个handler)

3.断开连接

基于可以直接处理socket传输的字节流这个特性,我们可以实现自己的应用层协议,通过一个handler,解析传入的信息,然后根据预先定义好的协议进行处理。netty不仅仅可以用来开发http服务器,有很多优秀的开源框架都是基于netty,然后实现了自己的协议,比如dubbo。

你可能感兴趣的:(Netty)