Netty-ServerBootstrap源码分析

带着疑问分析源码:ServerBootstrap bind的过程究竟做了什么,它是如何去接受请求处理请求的

###分析bind方法
#####粗略的跟踪ServerBootstrap的bind方法,画出时序图,红色标注了重要的步骤
这里写图片描述
可以发现,bind过程主要做了四个事情:

(1)创建ServerChannel,用于接收外部的连接请求:newChannel
(2)初始化channel:init(channel)
(3)将channel注册到线程池里:register
(4)绑定端口:doBind0

1. newChannel

这里写图片描述
newChannel的过程中会创建pipeline,pipeline中创建TailContext和HeadContext,并形成双向链表

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

这就是默认的pipeline,HeadContext实现了inbound/outbound,TailContext实现了inbound
这里写图片描述

2. init(channel)

观察ServerBootstrap的init方法,可以发现主要做了3个事情

//代码经过删减
void init(Channel channel) throws Exception {
	setChannelOptions(channel, options, logger); //1. 设置option
	channel.attr(key).set(e.getValue());         //2. 设置attribute
	ChannelPipeline p = channel.pipeline();      //3. 获取上一步中初始化好的pipeline
	p.addLast(new ChannelInitializer<Channel>() {//4. 为pipeline添加一个ChannelInitializer
	});
}

这里最重要的就是p.addLast,观察DefaultChannelPipeline的addLast方法

//代码经过删减
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {        
	newCtx = newContext(group, filterName(name, handler), handler);//1. 为handler构造一个handlerContext
	addLast0(newCtx);//2. 将handlerContext插入到tailContext的前面
	callHandlerAdded0(newCtx);//3. 调用handler的handlerAdd方法
}

而ChannelInitializer又是一种特殊的handler,它实际不会处理什么业务逻辑,当ChannelInitializer被加到pipeline后,触发了ChannelInitializer的handlerAdd方法,handlerAdd方法会先调用override的initChannel方法,然后把自己从pipeline中remove。
在ServerBootstrap的init方法中所添加的handler是这样的。

p.addLast(new ChannelInitializer<Channel>() {
    @Override
    public void initChannel(final Channel ch) throws Exception {
        final ChannelPipeline pipeline = ch.pipeline();
        ChannelHandler handler = config.handler();
        if (handler != null) {
            pipeline.addLast(handler);
        }
        ch.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                pipeline.addLast(new ServerBootstrapAcceptor(
                        ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
            }
        });
    }
});

initChannel会先往pipeline里加入用户设置的handler,然后最后再加入ServerBootstrapAcceptor。
如果此处用户没有设置handler,则此时的pipeline是这样的。
这里写图片描述

3. register

会向线程池申请一个线程并注册

//类名或对象名::调用方法
MultithreadEventLoopGroup::register			//1. 向线程池申请注册
MultithreadEventLoopGroup::next				//2. 获取下一个线程
AbstractUnsafe::register				//3. 交给Unsafe进行注册
AbstractSelectableChannel::register			//4. 最终的注册操作
ChannelPipeline::fireChannelRegistered			//5. 触发注册事件
4. doBind0(暂不详述)

绑定到某一个端口

###如何接受请求
观察NioEventLoop的processSelectedKey方法,得出接收到消息后的大致流程

//类名或对象名::调用方法
NioEventLoop::processSelectedKey                           //1. 开始处理消息
unsafe::read                                               //2. 读取消息
pipeline::fireChannelRead                                  //3. 交给channel对应的pipeline处理消息
AbstractChannelHandlerContext::invokeChannelRead(head, msg)//4. 从pipeline的head开始调用channelRead
ctx::findContextInbound                                    //5. 从head开始寻找第一个inbound的handler
ctx::fireChannelRead                                       //6. 将read事件传递给handler
handler::channelRead                                       //7. 最终的业务逻辑所在

一般handler的channelRead方法中,会在处理完消息后,选择性地把read时间传递给pipeline中的下一个inboundhandler。
所以,当ServerChannel接收到连接消息后,其对应pipeline中的ServerBootstrapAcceptor会处理channelRead事件。
此处,我们先看unsafe.read,对应NioMessageUnsafe的read方法

//代码有所删减
public void read() {
	int localRead = doReadMessages(readBuf);//1. 接收消息
	int size = readBuf.size();
	for (int i = 0; i < size; i ++) {
		readPending = false;
		pipeline.fireChannelRead(readBuf.get(i));//2. 交给pipeline处理
	}
}

这里是怎么处理消息的呢,观察NioServerSocketChannel的doReadMessages方法

protected int doReadMessages(List<Object> buf) throws Exception {
    SocketChannel ch = SocketUtils.accept(javaChannel());//接收连接请求,创建SocketChannel
    try {
        if (ch != null) {
            buf.add(new NioSocketChannel(this, ch));//封装成NioSocketChannel放到bufList里
            return 1;
        }
    } catch (Throwable t) {
        logger.warn("Failed to create a new channel from an accepted socket.", t);
        try {
            ch.close();
        } catch (Throwable t2) {
            logger.warn("Failed to close a socket.", t2);
        }
    }
    return 0;
}

可知ServerChannel为每一个连接请求创建NioSocketChannel,并传递下去处理。
观察ServerBootstrapAcceptor的channelRead方法

public void channelRead(ChannelHandlerContext ctx, Object msg) {
    final Channel child = (Channel) msg;
    child.pipeline().addLast(childHandler); //为pipeline添加handler
    setChannelOptions(child, childOptions, logger);//设置option
    for (Entry<AttributeKey<?>, Object> e: childAttrs) {
        child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());//设置attr
    }
    try {
        childGroup.register(child).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    forceClose(child, future.cause());
                }
            }
        });
    } catch (Throwable t) {
        forceClose(child, t);
    }
}

可以看出,这里做了类似初始化ServerChannel的事情:
(1)添加handler
(2)设置option
(3)设置attr
至此,连接请求处理完毕,生成的NioSocketChannel也会收到消息,然后交给pipeline处理消息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值