欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Netty源码分析 (二)----- ServerBootstrap

程序员文章站 2022-04-15 09:05:01
BootStrap在netty的应用程序中负责引导服务器和客户端。netty包含了两种不同类型的引导: 1. 使用服务器的ServerBootStrap,用于接受客户端的连接以及为已接受的连接创建子通道。 2. 用于客户端的BootStrap,不接受新的连接,并且是在父通道类完成一些操作。 一般服务 ......

bootstrap在netty的应用程序中负责引导服务器和客户端。netty包含了两种不同类型的引导:
1. 使用服务器的serverbootstrap,用于接受客户端的连接以及为已接受的连接创建子通道。
2. 用于客户端的bootstrap,不接受新的连接,并且是在父通道类完成一些操作。

一般服务端的代码如下所示:

simpleserver.java

/**
 * created by chenhao on 2019/9/4.
 */
public final class simpleserver {

    public static void main(string[] args) throws exception {
        eventloopgroup bossgroup = new nioeventloopgroup(1);
        eventloopgroup workergroup = new nioeventloopgroup();

        try {
            serverbootstrap b = new serverbootstrap();
            b.group(bossgroup, workergroup)
                    .channel(nioserversocketchannel.class)
                    .handler(new simpleserverhandler())
                    .childhandler(new simpleserverinitializer())
                    .option(channeloption.so_backlog, 128)
                    .childoption(channeloption.so_keepalive, true);

            channelfuture f = b.bind(8888).sync();

            f.channel().closefuture().sync();
        } finally {
            bossgroup.shutdowngracefully();
            workergroup.shutdowngracefully();
        }
    }
}

simpleserverhandler.java

private static class simpleserverhandler extends channelinboundhandleradapter {
    @override
    public void channelactive(channelhandlercontext ctx) throws exception {
        system.out.println("channelactive");
    }

    @override
    public void channelregistered(channelhandlercontext ctx) throws exception {
        system.out.println("channelregistered");
    }

    @override
    public void handleradded(channelhandlercontext ctx) throws exception {
        system.out.println("handleradded");
    }
}

simpleserverinitializer.java

public class simpleserverinitializer extends channelinitializer<socketchannel>{

    @override
    protected void initchannel(socketchannel ch) throws exception {
        channelpipeline pipeline = ch.pipeline();

        pipeline.addlast("framer", new delimiterbasedframedecoder(8192, delimiters.linedelimiter()));
        pipeline.addlast("decoder", new stringdecoder());
        pipeline.addlast("encoder", new stringencoder());
        pipeline.addlast("handler", new simplechatserverhandler());

        system.out.println("simplechatclient:" + ch.remoteaddress()+"连接上");
    }
}

在上篇博文(netty源码分析 (一)----- nioeventloopgroup)中 剖析了如下的两行代码内部的构造函数中干了些什么。

eventloopgroup bossgroup = new nioeventloopgroup(1);
eventloopgroup workergroup = new nioeventloopgroup();

具体可以见上篇博文,对于如上的两行代码得到的结论是:

1、 如果不指定线程数,则线程数为:cpu的核数*2

2、根据线程个数是否为2的幂次方,采用不同策略初始化chooser

3、产生nthreads个nioeventloop对象保存在children数组中。

可以理解nioeventloop就是一个线程,线程nioeventloop中里面有如下几个属性:

1、nioeventloopgroup (在父类singlethreadeventexecutor中)

2、selector

3、provider

4、thread (在父类singlethreadeventexecutor中)

更通俗点就是: nioeventloopgroup就是一个线程池,nioeventloop就是一个线程。nioeventloopgroup线程池中有n个nioeventloop线程。

serverbootstrap类分析

本篇博文将分析如下几行代码里面做了些什么。

serverbootstrap b = new serverbootstrap();
            b.group(bossgroup, workergroup)
                .channel(nioserversocketchannel.class)
                .handler(new simpleserverhandler())
                .childhandler(new simpleserverinitializer())
                .option(channeloption.so_backlog, 128)
                .childoption(channeloption.so_keepalive, true);

serverbootstrap类的继承结构如下:

Netty源码分析 (二)----- ServerBootstrap

该类的参数,有必要列出:

private final map<channeloption<?>, object> childoptions = new linkedhashmap<channeloption<?>, object>();
private final map<attributekey<?>, object> childattrs = new linkedhashmap<attributekey<?>, object>();
private volatile eventloopgroup childgroup;
private volatile channelhandler childhandler; 

其父类abstractbootstrap的参数

private volatile eventloopgroup group;
private volatile channelfactory<? extends c> channelfactory;
private volatile socketaddress localaddress;
private final map<channeloption<?>, object> options = new linkedhashmap<channeloption<?>, object>();
private final map<attributekey<?>, object> attrs = new linkedhashmap<attributekey<?>, object>();
private volatile channelhandler handler;  

下面主要看下这个链式设置相关的参数。

group(bossgroup, workergroup)

public serverbootstrap group(eventloopgroup parentgroup, eventloopgroup childgroup) {
    super.group(parentgroup);
    if (childgroup == null) {
        throw new nullpointerexception("childgroup");
    }
    if (this.childgroup != null) {
        throw new illegalstateexception("childgroup set already");
    }
    this.childgroup = childgroup;
    return this;
}

即将workergroup保存在 serverbootstrap对象的childgroup属性上。 bossgroup保存在serverbootstrap对象的group属性上

channel(nioserversocketchannel.class)

public b channel(class<? extends c> channelclass) {
    if (channelclass == null) {
        throw new nullpointerexception("channelclass");
    }
    return channelfactory(new bootstrapchannelfactory<c>(channelclass));
} 
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 为: bootstrapchannelfactory类的对象。其中这里bootstrapchannelfactory对象中包括一个clazz属性为:nioserversocketchannel.class,从如下该类的构造函数中可以明显的得到这一点。

private static final class bootstrapchannelfactory<t extends channel> implements channelfactory<t> {
    private final class<? extends t> clazz;

    bootstrapchannelfactory(class<? extends t> 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);
        }
    }

    @override
    public string tostring() {
        return stringutil.simpleclassname(clazz) + ".class";
    }
}

并且bootstrapchannelfactory中提供 newchannel()方法,我们可以看到 clazz.newinstance(),主要是通过反射来实例化nioserversocketchannel.class

handler(new simpleserverhandler())

public b handler(channelhandler handler) {
    if (handler == null) {
        throw new nullpointerexception("handler");
    }
    this.handler = handler;
    return (b) this;
}

注意:这里的handler函数的入参类是我们自己提供的。如下,后面的博文中将会分析这个handler将会在哪里以及何时被调用,这里只需要记住这一点即可

private static class simpleserverhandler extends channelinboundhandleradapter {
    @override
    public void channelactive(channelhandlercontext ctx) throws exception {
        system.out.println("channelactive");
    }

    @override
    public void channelregistered(channelhandlercontext ctx) throws exception {
        system.out.println("channelregistered");
    }

    @override
    public void handleradded(channelhandlercontext ctx) throws exception {
        system.out.println("handleradded");
    }
}

childhandler(new simpleserverinitializer())

public serverbootstrap childhandler(channelhandler childhandler) {
    if (childhandler == null) {
        throw new nullpointerexception("childhandler");
    }
    this.childhandler = childhandler;
    return this;
}

由最后一句可知,其实就是讲传入的childhandler赋值给serverbootstrap的childhandler属性。

该函数的主要作用是设置channelhandler来处理客户端的请求的channel的io。 这里我们一般都用channelinitializer这个类的实例或则继承自这个类的实例
这里我是通过新建类simplechatserverinitializer继承自channelinitializer。具体的代码如下:

public class simplechatserverinitializer extends channelinitializer<socketchannel>{

    @override
    protected void initchannel(socketchannel ch) throws exception {
        channelpipeline pipeline = ch.pipeline();

        pipeline.addlast("framer", new delimiterbasedframedecoder(8192, delimiters.linedelimiter()));
        pipeline.addlast("decoder", new stringdecoder());
        pipeline.addlast("encoder", new stringencoder());
        pipeline.addlast("handler", new simplechatserverhandler());

        system.out.println("simplechatclient:" + ch.remoteaddress()+"连接上");
    }
}

我们再看看channelinitializer这个类的继承图可知channelinitializer其实就是继承自channelhandler的 

Netty源码分析 (二)----- ServerBootstrap

 

可知,这个类其实就是往pipeline中添加了很多的channelhandler。

配置serverbootstrap的option

这里调用的是父类的abstractbootstrap的option()方法,源码如下:

public <t> b option(channeloption<t> option, t value) {
    if (option == null) {
        throw new nullpointerexception("option");
    }
    if (value == null) {
        synchronized (options) {
            options.remove(option);
        }
    } else {
        synchronized (options) {
            options.put(option, value);
        }
    }
    return (b) this;
}

其中最重要的一行代码就是:
options.put(option, value);
这里用到了options这个参数,在abstractbootstrap的定义如下:
private final map<channeloption<?>, object> options = new linkedhashmap<channeloption<?>, object>();
可知是私有变量,而且是一个map集合。这个变量主要是设置tcp连接中的一些可选项,而且这些属性是作用于每一个连接到服务器被创建的channel。

配置serverbootstrap的childoption

这里调用的是父类的serverbootstrap的childoption()方法,源码如下:

public <t> serverbootstrap childoption(channeloption<t> childoption, t value) {
    if (childoption == null) {
        throw new nullpointerexception("childoption");
    }
    if (value == null) {
        synchronized (childoptions) {
            childoptions.remove(childoption);
        }
    } else {
        synchronized (childoptions) {
            childoptions.put(childoption, value);
        }
    }
    return this;
}

这个函数功能与option()函数几乎一样,唯一的区别是该属性设定只作用于被acceptor(也就是boss eventloopgroup)接收之后的channel。

总结

比较简单哈,主要是将我们提供的参数设置到其相应的对象属性中去了。 因为后面会用到如下的几个属性,因此最好知道下,这些属性是何时以及在那里赋值的。

1、group:workergroup保存在 serverbootstrap对象的childgroup属性上。 bossgroup保存在serverbootstrap对象的group属性上

2、channelfactory:bootstrapchannelfactory类的对象(clazz属性为:nioserversocketchannel.class)

3、handler:simpleserverhandler

4、childhandler

5、option

6、childoption