《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》

摘要: 原创出处 http://ericcenblog.com/2018/01/12/jettyyuan-ma-pou-xi-xi-lie-5-serveryu-handler 「Eric Cen」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

本文主要从源代码的角度来分析Jetty的Connector如何通过ServerSocket来绑定和监听网络地址和端口的过程。

Jetty的Connector实现类是ServerConnector,循例从它的doStart()方法开始(至于是如何到达这个方法,请移步本系列的前几篇): img 它的doStart方法先调用父类AbstractNetworkConnectordoStart方法:img 在父类的doStart方法里面会调用子类实现的open方法,此处是ServerConnectoropen方法:img 到这里已经可以看到JettyConnectorJava NIO的关系了,将会调用openAcceptChannel方法来构建一个NIO的ServerSocketChannelimg 从上图的代码我们可以看到,Jetty默认用Java NIO来实现了它的网络框架,另外一点就是,如果你用java来实现一套网络框架,无非就是BIONIO两种,返朴归真。

先常规调用ServerSocketChannel的静态方法open方法来打开一个ServerSocketChannelimg因为Java的NIO是基于Selector来实现的,所以在这里会通过SelectorProvider类的静态方法provider来加载Selector的实现类:img它先尝试从System Property里加载Selector的实现类并实例化:img如果没有的话,就尝试通过SPI的方式来加载Selector的实现类并实例化:img如果还是没有的话,就用DefaultSelector,这个是根据底层操作系统来决定,比如,在Windows系统里将会是WindowsSelectorProvider这个类:imgimgWindowsSelectorProvider继承于SelectorProviderImpl这个抽象类, 它的openServerSocketChannel方法将会返回一个新建出来的ServerSocketChannelImpl对象:img

到此我们再回过头看ServerConnector的openAcceptChannel方法,img上图通过ServerSocketChannel.open()方法拿到的就是一个ServerSocketChannelImpl实例对象, 我们接着往下看,

img如果你没在启动参数里加--host参数,那它就会去指定所谓的全零地址:img

那这个所谓的anyLocalAddress又是怎么来的呢?img这里impl是InetAddressImpl,它是在加载InetAddress类的时候,通过它的类静态块初始化的:img而InetAddressImplFactory会通过一个本地方法isIPv6Supported来判断底层操作系统是否支持IPv6,来加载InetAddressImpl的实现类:imgimg在我的windows系统里,是支持IPv6的,所以返回的InetAddressImpl应该是Inet6AddressImpl对象,再来看它的anyLocalAddress方法:img这里会通过preferIPv6Address这个静态便来来判断应用有没有指定prefer使用IPv6地址,这个静态变量同样在InetAddress类的静态代码块里面初始化了:img我们可以看到,如果应用没有指定了java.net.preferIPv6Addresses这个-D参数的话,就算InetAddressImpl实例对象是Inet6AddressImpl,所谓的anyLocalAddress还是会用Inet4AddressImpl的anyLocalAddress:img而Inet4AddressImpl的anyLocalAddress是大名鼎鼎的0.0.0.0:img

好了,扯远了,我们再回到ServerConnector的openAcceptChannel方法:img在这里它就会拿出ServerSocketChannelImpl里的ServerSocket来进行网络地址和端口的绑定和监听! 我们先来看ServerSocketChannelImpl的socket()方法:img它会创建一个ServerSocket的适配对象:imgServerSocketAdaptor继承于ServerSocket。 我们接着来看ServerSocket的bind方法,它接收两个参数,一个是前面分析的InetSocketAddress,此处为IPv4的全零,0.0.0.0地址,一个是接收队列的size,此处为0(这个值在bind方法里面将会会重置),下面进入bind方法:img我们会看到接收队列的size会被重置为50,也就是说TCP接收队列里面最多只能有50个请求。我们接着要关注的是下面两行代码:img上面两行代码就是实现了网络地址的绑定和端口的监听。 我们先来看ServerSocket的getImpl方法:img首次调用的话,它会去调用createImpl方法:img这里impl是一个SocketImpl,它是通过调用setImpl方法来创建:img这里的factory是一个SocketImplFactory的静态变量,默认为null,而一路我们也没有设置,所以它还是为null,所以impl将会被新建为一个SocksSocketImpl的实例对象。 SocksSocketImpl继承于PlainSocketImpl,我们再看PlainSocketImpl的默认构造函数: img我们看到它会根据userDualStackImpl这个静态变量来实例化不同的AbstractPlainSocketImpl对象,而useDUalStackImpl这个静态变量也是在PlainSocketImpl的静态代码块里面初始化:img我们可以看到它是根据两个System Property值来确定的,一个是os.version,系统版本,我当前用的是JDK1.8的windows版, 另一个是java.net.preferIPv4Stack(这个跟我们上文看到的java.net.preferIPv6Addresses参数很相像,一个IPv4,一个IPv6),就是说如果系统版本(此处为windows版本)大于6.0并且应用没有指定参数java.net.preferIPv4Stack的话,useDualStackImpl为true,也就是说将会使用IPv4和IPv6双栈! 双栈的话 ,impl就会是DualStackPlainSocketImpl, 单栈的话impl就是TwoStacksPlainSocketImpl(其实这里我有点不明白,Dual和Two不都是有指两个的意思吗?望有识之士不吝赐教,私信告知。eric_cen@outlook.com), 再回到之前的bind方法,实际上调用的是AbstractPlainSocketImpl的bind方法,img而最终双栈和单栈的区别又在于DualStackPlainSocketImpl和TwoStacksPlainSocketImpl的socketBind方法。 DualStackPlainSocketImpl.socketBind方法: img最终调用的是本地方法bind0. TwoStacksPlainSocketImpl.socketBind方法: img最终调用的是本地方法socketBind。

而监听端口的listen方法最终也是分开DualStackPlainSocketImpl和TwoStacksPlainSocketImpl各自实现。

至此,Jetty的Connector如何绑定网络地址和监听端口分析完毕。其实本文大部分都是在分析Java自带的ServerSocket工作细节。

文章目录