博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Netty4.x 源码实战系列(三):NioServerSocketChannel全剖析
阅读量:6504 次
发布时间:2019-06-24

本文共 8080 字,大约阅读时间需要 26 分钟。

根据上一篇所述,在进行服务端开发时,必须通过ServerBootstrap引导类的channel方法来指定channel类型, channel方法的调用其实就是实例化了一个用于生成此channel类型对象的工厂对象。 并且在bind调用后,会调用此工厂对象来生成一个新channel。

本篇将通过NioServerSocketChannel实例化过程,来深入剖析NioServerSocketChannel。

在开始代码分析之前,我们先看一下NioServerSocketChannel的类继承结构图:

图片描述

调用工厂完成NioServerSocketChannel实例的创建

在绑定侦听端口过程中,我们调用了AbstractBootstrap的initAndRegister方法来完成channel的创建与初始化,channel实例化代码如下:

channelFactory.newChannel()

而channelFactory对象是我们通过ServerBootstrap.channel方法的调用生成的

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

通过代码可知,此工厂对象是ReflectiveChannelFactory实例

public class ReflectiveChannelFactory
implements ChannelFactory
{ private final Class
clazz; public ReflectiveChannelFactory(Class
clazz) { if (clazz == null) { throw new NullPointerException("clazz"); } this.clazz = clazz; } @Override public T newChannel() { try { return clazz.getConstructor().newInstance(); } catch (Throwable t) { throw new ChannelException("Unable to create Channel from class " + clazz, t); } }}

所以 channelFactory.newChannel() 实例化其实就是NioServerSocketChannel无参构造方法反射而成。

NioServerSocketChannel实例化过程分析

我们先看一下NioServerSocketChannel的无参构造代码

public NioServerSocketChannel() {    this(newSocket(DEFAULT_SELECTOR_PROVIDER));}

无参构造方法中有两个关键点:

1、使用默认的多路复用器辅助类 DEFAULT_SELECTOR_PROVIDER

private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();

2、通过newSocket创建ServerSocketChannel

private static ServerSocketChannel newSocket(SelectorProvider provider) {    try {               return provider.openServerSocketChannel();    } catch (IOException e) {        throw new ChannelException(                "Failed to open a server socket.", e);    }}

我们将newSocket生成的ServerSocketChannel对象继续传递给本类中的NioServerSocketChannel(ServerSocketChannel channel)构造方法

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

在其内部,我们会调用父类AbstractNioMessageChannel的构造方法:

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

因为是服务端新生成的channel,第一个参数指定为null,表示没有父channel,第二个参数指定为ServerSocketChannel,第三个参数指定ServerSocketChannel关心的事件类型为SelectionKey.OP_ACCEPT。

在AbstractNioMessageChannel内部会继续调用父类AbstractNioChannel的构造方法:

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {    // 继续调用父类构造方法    super(parent);    // 将ServerSocketChannel对象保存    this.ch = ch;    // 设置关心的事件    this.readInterestOp = readInterestOp;    try {        // 设置当前通道为非阻塞的        ch.configureBlocking(false);    } catch (IOException e) {        try {            ch.close();        } catch (IOException e2) {            if (logger.isWarnEnabled()) {                logger.warn(                        "Failed to close a partially initialized socket.", e2);            }        }        throw new ChannelException("Failed to enter non-blocking mode.", e);    }}

在AbstractNioChannel中做了下面几件事:

1、继续调用父类AbstractChannel(Channel parent)构造方法;
2、通过this.ch = ch 保存ServerSocketChannel, 因为NioServerSocketChannel是Netty封装的对象,而ServerSocketChannel是有前面默认selector_provider生成的,是java nio的, 其实“this.ch = ch”可以被认为是绑定java nio服务端通道至netty对象中;
3、设置ServerSocketChannel关心的事件类型;
4、设置ServerSocketChannel为非阻塞的(熟悉Java NIO的都知道如果不设置为false,启动多路复用器会报异常)

我们再看一下AbstractChannel(Channel parent)的内部代码细节

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

此构造方法中,主要做了三件事:

1、给channel生成一个新的id
2、通过newUnsafe初始化channel的unsafe属性
3、newChannelPipeline初始化channel的pipeline属性

id的生成我们就不细究了,我们主要看看newUnsafe 及 newChannelPipeline是如何创建unsafe对象及pipeline对象的。

newUnsafe()方法调用

在AbstractChannel类中,newUnsafe()是一个抽象方法

protected abstract AbstractUnsafe newUnsafe();

通过上面的类继承结构图,我们找到AbstractNioMessageChannel类中有newUnsafe()的实现

@Overrideprotected AbstractNioUnsafe newUnsafe() {    return new NioMessageUnsafe();}

此方法返回一个NioMessageUnsafe实例对象,而NioMessageUnsafe是AbstractNioMessageChannel的内部类

private final class NioMessageUnsafe extends AbstractNioUnsafe {    private final List readBuf = new ArrayList();    @Override    public void read() {        assert eventLoop().inEventLoop();        final ChannelConfig config = config();        final ChannelPipeline pipeline = pipeline();        final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();        allocHandle.reset(config);        boolean closed = false;        Throwable exception = null;        try {            try {                do {                    int localRead = doReadMessages(readBuf);                    if (localRead == 0) {                        break;                    }                    if (localRead < 0) {                        closed = true;                        break;                    }                    allocHandle.incMessagesRead(localRead);                } while (allocHandle.continueReading());            } catch (Throwable t) {                exception = t;            }            int size = readBuf.size();            for (int i = 0; i < size; i ++) {                readPending = false;                pipeline.fireChannelRead(readBuf.get(i));            }            readBuf.clear();            allocHandle.readComplete();            pipeline.fireChannelReadComplete();            if (exception != null) {                closed = closeOnReadError(exception);                pipeline.fireExceptionCaught(exception);            }            if (closed) {                inputShutdown = true;                if (isOpen()) {                    close(voidPromise());                }            }        } finally {            if (!readPending && !config.isAutoRead()) {                removeReadOp();            }        }    }}

NioMessageUnsafe 只覆盖了 父类AbstractNioUnsafe中的read方法,通过NioMessageUnsafe 及其父类的代码便可以知道, 其实unsafe对象是真正的负责底层channel的连接/读/写等操作的,unsafe就好比一个底层channel操作的代理对象。

newChannelPipeline()方法调用

newChannelPipeline直接在AbstractChannel内实现

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

该方法返回了创建了一个DefaultChannelPipeline对象

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对象会绑定NioServerSocketChannel对象,并初始化了HeadContext及TailContext对象。

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

head及tail初始化完成后,它们会相互连接。

通过上面的代码可以得出,pipeline就是一个双向链表。关于Pipeline的更多细节,此处不做赘述,欢迎大家关注下一篇文章。

我们在回到NioServerSocketChannel的构造方法 NioServerSocketChannel(ServerSocketChannel channel)

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

父类构造方法调用完成后,NioServerSocketChannel还要初始化一下自己的配置对象

config = new NioServerSocketChannelConfig(this, javaChannel().socket());

NioServerSocketChannelConfig是NioServerSocketChannel的内部类

private final class NioServerSocketChannelConfig extends DefaultServerSocketChannelConfig {    private NioServerSocketChannelConfig(NioServerSocketChannel channel, ServerSocket javaSocket) {        super(channel, javaSocket);    }    @Override    protected void autoReadCleared() {        clearReadPending();    }}

而NioServerSocketChannelConfig 又是继承自DefaultServerSocketChannelConfig,通过代码分析,此config对象就是就会对底层ServerSocket一些配置设置行为的封装。

至此NioServerSocketChannel对象应该创建完成了~

总结:

1、NioServerSocketChannel对象内部绑定了Java NIO创建的ServerSocketChannel对象;

2、Netty中,每个channel都有一个unsafe对象,此对象封装了Java NIO底层channel的操作细节;

3、Netty中,每个channel都有一个pipeline对象,此对象就是一个双向链表;

转载地址:http://adqyo.baihongyu.com/

你可能感兴趣的文章
PHP学习笔记 第八讲 Mysql.简介和创建新的数据库
查看>>
【git】git入门之把自己的项目上传到github
查看>>
js获取鼠标位置
查看>>
2016.8.11 DataTable合并及排除重复方法
查看>>
php 魔术方法 说明
查看>>
Mysql
查看>>
POJ-1860-Currency Exchange
查看>>
跨越企业的“中等收入陷阱”
查看>>
Android 开发者必知的开发资源
查看>>
软件工程技术基础-(软件复用技术)
查看>>
给django视图类添加装饰器
查看>>
简述 clearfix 的原理
查看>>
【Project Euler】530 GCD of Divisors 莫比乌斯反演
查看>>
luogu P1280 尼克的任务 序列DP
查看>>
iphone UIView的一些基本方法理解
查看>>
sys.check_constraints
查看>>
vue问题
查看>>
ThinkPHP 框架学习
查看>>
css3箭头效果
查看>>
MathType在手,公式不求人!
查看>>