Netty启动步骤绑定端口示例方法源码分析

目录
  • 绑定端口
    • 我们继续跟第一小节的最初的doBind()方法
    • 第二步, 获得channel
    • 重点关注下doBind(localAddress)方法
    • 最终会走到这一步, pipeline.fireChannelActive()
  • 章节总结

前文传送门:Netty启动流程注册多路复用源码解析

绑定端口

上一小节我们学习了channel注册在selector的步骤, 仅仅做了注册但并没有监听事件, 事件是如何监听的呢?

我们继续跟第一小节的最初的doBind()方法

private ChannelFuture doBind(final SocketAddress localAddress) {
    //初始化并注册(1)
    final ChannelFuture regFuture = initAndRegister();
    //获得channel(2)
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
        return regFuture;
    }
    if (regFuture.isDone()) {
        ChannelPromise promise = channel.newPromise();
        //绑定(3)
        doBind0(regFuture, channel, localAddress, promise);
        return promise;
    } else {
        //去除非关键代码
        return promise;
    }
}

上一小节跟完了initAndRegister()方法, 我们继续往下走:

第二步, 获得channel

final Channel channel = regFuture.channel();

通过ChannelFuture的channel()方法获得了我们刚刚注册的NioServerSocketChannel, 拿到这个channel我们跟到第三步, 绑定

跟进方法doBind0(regFuture, channel, localAddress, promise):

private static void doBind0(final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) {
    channel.eventLoop().execute(new Runnable() {
        @Override
        public void run() {
            if (regFuture.isSuccess()) {
                //绑定端口
                channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            } else {
                promise.setFailure(regFuture.cause());
            }
        }
    });
}

最终会走到channel.bind(localAddress, promise)这个方法当中

继续跟, 会走到AbstractChannel的bind()方法中:

public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    //通过pipeline绑定端口
    return pipeline.bind(localAddress, promise);
}

这里的pipeline就是channel初始化创建的pipline, pipline是事件传输通道, 这里我们暂不跟传输过程, 我们只需知道最后这个方法走到了AbstractChannel的bind()方法

跟到AbstractChannel的bind()方法:

public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
    //代码省略
    //端口绑定之前不是active, 返回false
    boolean wasActive = isActive();
    try {
        //做jdk底层的绑定
        doBind(localAddress);
    } catch (Throwable t) {
        //省略
        return;
    }
    //端口绑定之前不是active, 端口绑定之后变成active了
    if (!wasActive && isActive()) {
        invokeLater(new Runnable() {
            @Override
            public void run() {
                pipeline.fireChannelActive();
            }
        });
    }
    safeSetSuccess(promise);
}

重点关注下doBind(localAddress)方法

跟到NioSeverSocketChannel的doBind()方法:

protected void doBind(SocketAddress localAddress) throws Exception {
    //jdk版本的判断
    if (PlatformDependent.javaVersion() >= 7) {
        javaChannel().bind(localAddress, config.getBacklog());
    } else {
        javaChannel().socket().bind(localAddress, config.getBacklog());
    }
}

开始是一个jdk版本的判断, 我们以jdk7以上为例, 看到这条语句:

javaChannel().bind(localAddress, config.getBacklog());

终于找到了和jdk底层相关的绑定逻辑了, javaChannel()返回的是当前channel绑定的jdk底层的channel, 而bind()方法, 就是jdk底层的channel绑定端口的逻辑

回到bind(final SocketAddress localAddress, final ChannelPromise promise)方法:

首先看if判断: if (!wasActive && isActive())

这里意思是如果之前不是active, 绑定之后是active的话, 执行if块, 显然这里符合条件, 继续往里走

最终会走到这一步, pipeline.fireChannelActive()

这也是传输active事件, 目前我们只需知道, 事件完成之后, 会调用AbstractChannel内部类AbstractUnsafe的beginRead()方法

跟到AbstractUnsafe的beginRead()方法中:

public final void beginRead() {
    assertEventLoop();
    if (!isActive()) {
        return;
    }
    try {
        doBeginRead();
    } catch (final Exception e) {
        //代码省略
    }
}

我们关注doBeginRead()方法:

protected void doBeginRead() throws Exception {
    //拿到selectionKey
    final SelectionKey selectionKey = this.selectionKey;
    if (!selectionKey.isValid()) {
        return;
    }
    readPending = true;
    //获得感兴趣的事件
    final int interestOps = selectionKey.interestOps();
    //判断是不是对任何事件都不监听
    if ((interestOps & readInterestOp) == 0) {
        //此条件成立
        //将之前的accept事件注册, readInterest代表可以读取一个新连接的意思
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}

这里到了jdk底层的调用逻辑, 通过注释不难看出其中的逻辑, 我们拿到和channel绑定的jdk底层的selectionKey, 获取其监听事件, 一上节我们知道, channel注册的时候没有注册任何事件, 所以我们这里if  ((interestOps & readInterestOp) == 0) 返回true, 之后, 将accept事件注册到channel中, 也就是 selectionKey.interestOps(interestOps | readInterestOp) 这步执行的

注册完accept事件之后, 就可以轮询selector, 监听是否有新连接接入了

章节总结

通过了这一章的学习, 我们了解了server启动的大概流程, 这里重点掌握整个启动脉络, 知道关键步骤在哪个类执行, 后面的章节会分析每一个模块的含义

以上就是Netty启动步骤绑定端口源码分析的详细内容,更多关于Netty启动的资料请关注我们其它相关文章!

(0)

相关推荐

  • Netty分布式server启动流程Nio创建源码分析

    目录 NioServerSocketChannel创建 继承关系 绑定端口 端口封装成socket地址对象 跟进initAndRegister()方法 创建channel 父类的构造方法 将jdk的channel设置为非阻塞模式 前文传送门 Netty分布式Server启动流程服务端初始化源码分析 NioServerSocketChannel创建 我们如果熟悉Nio, 则对channel的概念则不会陌生, channel在相当于一个通道, 用于数据的传输 Netty将jdk的channel进行了

  • Netty源码分析NioEventLoop处理IO事件相关逻辑

    目录 NioEventLoop的run()方法: processSelectedKeys()方法 processSelectedKeysOptimized(selectedKeys.flip())方法 processSelectedKey(k, (AbstractNioChannel) a)方法 之前我们了解了执行select()操作的相关逻辑, 这一小节我们继续学习轮询到io事件的相关逻辑: NioEventLoop的run()方法: protected void run() { for (;

  • Netty启动流程服务端channel初始化源码分析

    目录 服务端channel初始化 回顾上一小节initAndRegister()方法 init(Channel)方法 前文传送门 Netty分布式server启动流程 服务端channel初始化 回顾上一小节initAndRegister()方法 final ChannelFuture initAndRegister() { Channel channel = null; try { //创建channel channel = channelFactory.newChannel(); //初始化

  • Netty分布式Server启动流程服务端初始化源码分析

    目录 第一节:服务端初始化 group方法 初始化成员变量 初始化客户端Handler 第一节:服务端初始化 首先看下在我们用户代码中netty的使用最简单的一个demo: //创建boss和worker线程(1) EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); //创建ServerBootstrap(2) ServerBootst

  • 使用Netty搭建服务端和客户端过程详解

    前言 前面我们介绍了网络一些基本的概念,虽然说这些很难吧,但是至少要做到理解吧.有了之前的基础,我们来正式揭开Netty这神秘的面纱就会简单很多. 服务端 public class PrintServer { public void bind(int port) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); //1 EventLoopGroup workerGroup = new NioEventLo

  • Netty启动步骤绑定端口示例方法源码分析

    目录 绑定端口 我们继续跟第一小节的最初的doBind()方法 第二步, 获得channel 重点关注下doBind(localAddress)方法 最终会走到这一步, pipeline.fireChannelActive() 章节总结 前文传送门:Netty启动流程注册多路复用源码解析 绑定端口 上一小节我们学习了channel注册在selector的步骤, 仅仅做了注册但并没有监听事件, 事件是如何监听的呢? 我们继续跟第一小节的最初的doBind()方法 private ChannelFu

  • 解析ConcurrentHashMap: put方法源码分析

    上一章:预热(内部一些小方法分析) put()方法是并发HashMap源码分析的重点方法,这里涉及到并发扩容,桶位寻址等等- JDK1.8 ConcurrentHashMap结构图: 1.put方法源码解析 // 向并发Map中put一个数据 public V put(K key, V value) { return putVal(key, value, false); } // 向并发Map中put一个数据 // Key: 数据的键 // value:数据的值 // onlyIfAbsent:

  • 解析ConcurrentHashMap: transfer方法源码分析(难点)

    上一篇文章介绍过put方法以及其相关的方法,接下来,本篇就介绍一下transfer这个方法(比较难),最好能动手结合着源码进行分析,并仔细理解前面几篇文章的内容~ 注:代码分析的注释中的CASE0.CASE1- ,这些并没有直接关联关系,只是为了给每个if逻辑判断加一个标识,方便在其他逻辑判断的地方进行引用而已. 再复习一下并发Map的结构图: 1.transfer方法 transfer方法的作用是:迁移元素,扩容时table容量变为原来的两倍,并把部分元素迁移到其它桶nextTable中.该方

  • Hadoop源码分析二安装配置过程详解

    目录 1. 创建用户 2. 安装jdk 3. 修改hosts 4. 配置ssh免密登录 5. 安装zookeeper 解压: 修改配置文件 修改内容如下: 配置环境变量 启动 6. 安装hadoop 对于三台节点的配置安排如下: 解压: 修改配置文件: 修改core-site.xml 配置hdfs-site.xml 配置mapred-site.xml 配置yarn-site.xml 配置slaves 7. 初始化 在初始化前需要将所有机器都配置好hadoop (1) 启动zookeeper (2

  • Java并发编程之Condition源码分析(推荐)

    Condition介绍 上篇文章讲了ReentrantLock的加锁和释放锁的使用,这篇文章是对ReentrantLock的补充.ReentrantLock#newCondition()可以创建Condition,在ReentrantLock加锁过程中可以利用Condition阻塞当前线程并临时释放锁,待另外线程获取到锁并在逻辑后通知阻塞线程"激活".Condition常用在基于异步通信的同步机制实现中,比如dubbo中的请求和获取应答结果的实现. 常用方法 Condition中主要的

  • Netty源码分析NioEventLoop线程的启动

    目录 之前的小节我们学习了NioEventLoop的创建以及线程分配器的初始化, 那么NioEventLoop是如何开启的呢, 我们这一小节继续学习 NioEventLoop的开启方法在其父类SingleThreadEventExecutor中的execute(Runnable task)方法中, 我们跟到这个方法: @Override public void execute(Runnable task) { if (task == null) { throw new NullPointerEx

  • Spring SpringMVC在启动完成后执行方法源码解析

    关键字:spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件) 应用场景:很多时候我们想要在某个类加载完毕时干某件事情,但是使用了spring管理对象,我们这个类引用了其他类(可能是更复杂的关联),所以当我们去使用这个类做事情时发现包空指针错误,这是因为我们这个类有可能已经初始化完成,但是引用的其他类不一定初始化完成,所以发生了空指针错误,解决方案如下: 1.写一个类继承spring的ApplicationListener监听,并监控ContextRefresh

随机推荐