Jetty9源码剖析 - Connector组件 - ServerConnector

转载自ph0ly:http://www.ph0ly.com

一、ServerConnector的概念

ServerConnector顾名思义是服务端连接器,是Jetty容器核心组件,它主要完成客户端与服务端的连接生命周期管理,能够处理HTTP,HTTP/2和WebSocket,或SSL协议的连接与通信

二、继承体系

继承图

ServerConnector继承了AbstractNetworkConnector,同时AbstractNetworkConnector继承了AbstractConnector,实现了NetworkConnector,NetworkConnector继承了Connector,AbstractConnector也实现了Connector接口

三、总体架构

架构图

连接接入:通过ServerConnector创建的ServerSocketChannel,每个Acceptor会共享该ServerSocketChannel,每个Acceptor在一个线程中循环执行ServerSocketChannel.accept,当接受到一个连接后,会触发ServerConnector的accepted方法,这时候会调用ServerConnectorManager(实际是一个SelectorManager)选择一个ManagedSelector来注册该SocketChannel读写事件,并让ManagedSekector来创建与该SocketChannel相关的SelectChannelEndPoint和HttpConnection

读事件:连接接入时已经将该连接的SocketChannel注册到某一个ManagedSelector上,因此ManagedSelector会在后续轮询(其实是通过EPC来完成生产的,这里简化了模型,方便读者理解)到该读事件,并调用SelectChannelEndPoint.onSelected来处理读操作,这时候会回调HttpConnection.onFillable,然后触发HttpParser.parse来解析Http协议头,以及后续的Handler执行

写事件(由于线太多容易乱,图上很难画出,所以这里用文字描述):和读事件有些类似,写事件是在应用程序Servlet执行完成后,调用HttpServletResponse的OutputStream.write方法完成数据回写,这时候会触发HttpOutput.write,之后触发HttpTransport(HttpChannel)的send方法,最终触发到SelectChannelEndPoint.write,最后将数据写入之前注册的SocketChannel,这样数据就回写到客户端

四、源码分析

1. 构造函数

构造函数-1

构造函数有很多,实际就调用到这个构造函数,因此这里分析这一个
第一行调用到父类AbstractNetworkConnector,而AbstractNetworkConnector也继续调用父类AbstractConnector的构造函数,后面分析父类的构造方法做了什么

创建SelectorManager,其实就是调用如图方法,创建ServerConnectorManager,是ServerConnector的内部类
SelectorManager

由于ServerConnectorManager也是具有生命周期,因此扔到容器托管

最后设置Acceptor的线程的运行优先级,将Acceptor线程调至低优先级,保证其他线程优先执行

再来看父类AbstractConnector的构造函数:
AbstractConnector构造函数

从图中可以看到AbstractConnector实现了绝大部分Connector的逻辑

之前架构图中已经看到,Executor线程池、Scheduler定时器、ByteBufferPool缓冲池,三大组件这里判断是否用户指定了,如果没指定就使用默认的,如果Executor没指定,就使用Server带的线程池,也就是QueuedThreadPool(简称:qtp),如果Scheduler没指定,则使用ScheduledExecutorScheduler(Jetty基于JDK的ScheduledThreadPoolExecutor实现的定时器线程池),如果ByteBufferPool没指定,则使用ArrayByteBufferPool,将它们3个扔到Connector容器里面托管(ArrayByteBufferPool不具有生命周期,因此就是一个IOC控制)

如果用户指定了多个ConnectionFactory,这里还要将这些ConnectionFactory添加到该Connector,_factories是一个LinkedHashMap,将不同协议的映射到ConnectionFactory,这里就不细讲了

之后计算Acceptor个数和与之对应的线程个数,计算规则是基于,max(1, min(4, JVM的可用处理器数/8)),可以看得出最大就4个,最少1个,用户也可以自己指定,如果指定了,就以用户的为准(但是建议不要超过JVM可用处理器数)

2. 启动(doStart)

doStart-1

第一行调用父类,待会儿讲解,先来分析getAcceptors如果为0,也就是之前用户设置了acceptors=0,那就会将这个ServerSocketChannel放到Selector来监听Accept事件,注意这里和前面的不一样,默认都是AbstractConnector里面的Acceptor来阻塞ServerSocketChannel.accept接受连接,如果不设置acceptors就会使用ManagedSelector来处理连接的接受,这样的话就将之前文章说的IO模型退化为多线程Reactor IO模型

再来看AbstractNetworkConnector.doStart
doStart-2

第一行调回到ServerConnector
doStart-open-1

doStart-open-2

ServerConnector.open会开一个新的ServerSocketChannel,配置为阻塞模式,并托管IOC容器
ServerConnector.openAcceptChannel其实就是根据用户的配置来监听地址和端口

再回来看AbstractNetworkConnector.doStart,调用了父类的AbstractConnector.doStart

doStart-3

这里拿到默认协议的默认连接工厂(默认当然就是Http1.x协议了)
然后拿SslConnectionFactory,如果有就校验ssl协议的下一个协议,很明显ssl协议只是安全层,需要真实的后续协议,如Http、或者Http2来支持
之后super.doStart就不讲了,生命周期的
_stopping是停止的时候用的,这里就不细讲
之后根据之前配置好的_acceptor个数来创建Acceptor,注意这里把他们扔到了线程池执行,并不是之前我们Thread[]实体线程来执行,看起来挺坑的

3. 连接接入

accept-1

accept-2

可以看到上面启动的时候,已经将Acceptor扔到线程池执行了,自然这里的run方法要运行起来
上面初始化的代码就不解释,很简单,我们直接看while循环里面在干啥
如果在接受状态,就会一直执行accept方法,这就是Acceptor唯一要做的事情,再来看看accept怎么实现的
accept-3

accept方法调回了ServerConnector.accept方法,如上图
这里阻塞调用ServerSocketChannel.accept,获取到一个连接后,调用accepted方法
accept-4

这里将新的连接配置为非阻塞模式,并配置TCP NoDelay参数,并调用SelectorManager来分配这个连接对应的ManagedSelector,并创建该连接的EndPoint和Connection,后续的SelectorManager和ManagedSelector会详细分析这块逻辑
如果想现在了解SelectorManager,点我
如果想现在了解ManagedSelector,点我

4. 数据读写与Http处理

由于后面的文章会详细分析数据读写,因此这里不再讲解,请关注后续的文章
HttpConnection
SelectChannelEndPoint

五、总结

总体来说ServerConnector是基于NIO的同步非阻塞IO模型,配合Callbacks来模拟了一套异步IO的处理框架。它是连接接受的入口,利用SelectorManager分配Selector,但数据读写是ManagedSelector来管理,这样就完成连接与读写事件的分离,增强了健壮性及高并发性能。后面会详细讲解SelectorManager连接的分发,以及ManagedSelector对读写事件的处理,欢迎大家持续关注~

猜你喜欢

转载自blog.csdn.net/qq_41084324/article/details/83343030