• Netty源码剖析-启动服务


    参考文献:极客时间傅健老师的《Netty源码剖析与实战》Talk is cheap.show me the code!

    --1主线分两步:

      一:首先在our thread里,如果写在main方法中则就是main thread;

    ①:创建selector;

    ②:创建server socket channel;

    ③:初始化server socket channel;

    ④:给server socket channel 从boss  group中选择一个NioEventLoop;

      二:boss thread:

    ⑤:将server socket channel注册到选择的NioEventLoop的selector(上一步创建的selector)

    ⑥:绑定地址启动

    ⑦:注册接收连接事件(OP_ACCEPT)到selector上

    --2源码演示:

      首先在代码启动的地方加断点,并且在NioEventLoop.java中的openSelector()上也加一个断点(用于验证selector什么时候被创建),如下图:

    Debug启动后会进入上图第一个断点,放行后进入openSelector()的这个断点上,通过观察Frames区域可以发现selector是在刚才第一个断点的时候就被创建了,即“EventLoopGroup bossGroup = new NioEventLoopGroup();”的时候:

    好的,继续放行之后则回来到开启服务的断点:

    不难看出有个sync(),它表示阻塞,也就是说启动本身是个异步的过程,.sync()代表要启动完才能进行下一步;接下来进入bind()方法看看,一直跟进,直到进入doBind();在如图的两个地方加上断点:

    上图的“final ChannelFuture regFuture = initAndRegister();”;initAndRegister()实际表示三步:①创建一个server socket channel,②初始化server socket channel,③将server socket channel注册到NioEventLoop的selector上。创建完成之后返回的是regFuture;通过名字“Future”就可以知道它是异步过程;所以上图第一个断点不一定能注册完成,因为注册是丢到NioEventLoop里面去执行去了,所以有个第二个断点:等着注册完成之后来通知再执行bind。然后我们跟进去看看initAndRegister():

    不难看出先通过工厂创建后初始化,接着跟进init(channel)方法:挨个下一步可以看到这段代码:

    这里的ChannelInitializer一次性,初始化hander,负责添加一个ServerBootstrapAcceptor hander,添加完成后自己就移除了,其中ServerBootstrapAcceptor hander负责接收客户端连接创建连接后,对连接的初始化工作。跳回去之后可以看到:

    "ChannelFuture regFuture = config().group().register(channel);"的group()就是boss group,跟进register()之后有这个代码,在图中的register0()这边加个断点:

    然后跟进eventLoop.execute()方法:

    接着进入startThread();

      走到这才是正正的启动线程,接着放行则会执行register0();

    再跟进register0()方法

    跟进doRegister():

    这里不难发现它真正在处理了,然后先跳回去,下面这图就是通知我们成功了

    继续往下走则会到之前打的一个断点(doBind0());

    然后继续跟进:

    在channel.bind()加一个断点:然后紧接着跳过来在进入bind()方法:

    可以看到pipeline.bind();Netty是串行化的操作,pipeline里面有各种各样的hander,除了上图截图显示的hander,每个pipeline都有head和tail 的hander;如下显示:

    这里我们跳到head的hander;操作如下:

    跳过来之后查找bind()方法如图,

    找到之后在unsafe.bind()加一个断点:

    跳过来进入bind()方法看看:

    可以看到这一步:do开头的几乎都是表示执行!在跟进去看看:

    这时候就能看到bind了,然后继续

    这里的就是绑定后开始激活,在pipeline.fireChannelActive()加一个断点,跳过来后:

    和之前一样,跳到head上;然后查找ChannelActive();

    打上断点如图,接着跳过来,其中readIfIsAutoRead()就是注册读事件,读包括:创建连接、读数据,这里指的是创建连接。跟进方法:

    继续跟进:

    能看出pipeline.read();这时候打开之前跳转过来的pipeline的head里找到read();

     加个断点接着跳过来,这里unsafe.beginRead();实际上就是注册OP_ACCEPT/OP_READ事件;创建连接或者读事件。接着跟进:

    实际它是调用的doBeginRead();

     继续跟进:

     走到这里就开始注册OP_ACCEPT=16。

    --3总结:启动服务的本质:

      Selector selector = sun.nio.ch.SelectorProviderImpl.openSelector();

      ServerSocketChannel serverSocketChannel = provider.openServerSocketChannel();

      selectionKey = javaChannel().register(eventLoop().unwrappedSelector(),0,this);

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

      selectionKey.interestOps(OP_ACCEPT);

    Selector是在new NioEventLoopGroup()(创建一批NioEventLoop)时创建的

    第一次Register并不是监听OP_ACCEPT,而是0;

      selectionKey = javaChannel().register(eventLoop().unwrappedSelector(),0,this);

    最终监听OP_ACCEPT是通过bind完成后的fireChannelActive()来触发的。

    NioEventLoop是通过Register操作的执行来完成启动的

    类似ChannelInitializer,一些Hander可以设计成一次性的,用完就移除,比如授权。

    我只想做的更好,仅此而已。

  • 相关阅读:
    SER SERVER存储过程
    SQL SERVER连接、合并查询
    delete drop truncate 区别
    将一个表中的数据插入到另外的新表中
    strtol函数 将字符串转换为相应进制的整数
    malloc函数及用法
    求亲密数
    牛顿迭代法求开根号。 a^1/2_______Xn+1=1/2*(Xn+a/Xn)
    C语言中用于计算数组长度的函数 “strlen() ”。
    如何给sublime text3安装汉化包?so easy 哦
  • 原文地址:https://www.cnblogs.com/-qilin/p/11807450.html
Copyright © 2020-2023  润新知