⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 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工作细节。

文章目录