Netty源码分析--Channel注册&绑定端口(下)(七)

      接下来,我们看到的就是两个非常重要的方法

      

      就是 processSelectedKeys() 和  runAllTasks() 方法了。

      selectionKey中ready的事件,如accept、connect、read、write等,由processSelectedKeys方法触发。属于I/O任务。

      添加到taskQueue中的任务,如register0、bind0等任务,由runAllTasks方法触发。属于非I/O任务。

      两种任务的执行时间比由变量ioRatio控制,默认为50,则表示允许非IO任务执行的时间与IO任务的执行时间相等。

      我们看一下 processSelectedKeys() 方法, 因为 selectedKeys != null 所以进入  processSelectedKeysOptimized() 方法。

      由于没有这里只是启动服务端,没有客户端接入进来,所以我们先跳过processSelectedKeys(),一会我们结合客户端接入来讲这里。

      直接看 runAllTasks() 方法。

      

        Runnable task = pollTask(); 这个就是从 taskQueue 中拿出一个task。

        然后循环执行这个任务, safeExecute(task)。

        

       这个方法也是很简单,就是直接执行Runnable接口中的run()方法(这里并不是启动一个线程,而是仅仅的执行一个普通的run方法)。

       大家想一下这里的这个task应该是什么呢?

        

        大家还记得这段代码吗? 就是这个 register0() 方法。

       

         我们先进入到 doRegister() 方法

         

         继续传入当前的eventloop中的selector, opt = 0,  第三个参数 this 就是当前的 NioServerSocketChannel。 进入register 方法

        

           大家看我圈出来的这一句,熟悉吗?我当时将NIO的时候是不是讲到了。

           这里就是把当前的channel注册到这个多路复用器上。并且把 NioServerSocketChannel 传进去当做附件 attach, 注册的 interestOps = 0 

          好了,当执行完task,由于是一个死循环,那么会继续执行刚刚的整个过程。

         

           好了,总结一下: 也就是说有一个线程一直在这里不断循环的等待新的 selectionKey中ready的事件,如accept、connect、read、write等。 如果有待处理的task,将会去优先处理的task.

           一会我们会启动一个客户端看一下是怎么交互的。

           整个注册完成之后,接下来就是 绑定端口 ,将服务对外开放出去。

           我们看下AbstractBootstrap中的  doBind() 方法。

            

              由于整个注册过程是异步的,所以这里 regFuture.isDone() 是否已经完成,如果完成直接执行doBind0(),如果没有完成,那么就监听异步响应方法,等待成功之后,再执行doBind0()方法。

             我们进入doBind0()方法

             

           我们看其实就是向eventLoop中的任务队列中添加一个task。

           这里我们debug来看一下

          另外在 AbstractBootstrap中打一个断点,在这里等待注册事件先完成。

          

         好的,我们启动服务端。

         

        断点进来了, 我们再在  NioEventLoop 中打一个断点,因为这里是处理task的地方

           

           我们发现有一个主线程,一个子线程,如下图

          

         切换到子线程,我们看下 task 的执行过程。

         

       因为switch中的hasTask() 是true,那么我们就直接看

       

       

     从任务队列中取出一个task,我们看到就是刚刚我们的那个任务。然后通过safeExecute(task)执行run方法

      

      继续F5。我们看进入到了runnable中的run方法。

   

     接下来就是一段链式调用,链式访问pipleline中的handler         TailContext -> ServerBootstrapAcceptor -> LoggingHandler -> HeadContext

     

     TailContext 和 ServerBootstrapAcceptor 中没有bind方法,直接进入LoggingHandler的bind方法,打一个日志

    

   继续f5进入到 HeadContext中的bind方法

  

  

   先判断是否激活,如果没有,则稍后链式调用handlers中的 channelActive()方法。

   进入doBind方法

   

   ok,到这里绑定端口成功。

  目前为止,Server服务端启动完成,接下来我们看一下,一个客户端是怎么接入进来并且进行读写操作的。

posted @ 2019-07-04 00:07  Diligent_Watermelon  阅读(895)  评论(0编辑  收藏  举报