Connector在Jetty中是负责接收客户端请求,然后为每个请求分配一个对请求进行处理的线程,并且负责将处理完得到的响应发送给客户端。总共有两种类型的Connector,基于Socket的阻塞Connector,代表类是SocketConnector;另外就是基于非阻塞NIO的SelectChannelConnector,当然其实在Jetty中还有基于NIO的阻塞Connector。但现在使用最多的就是SelectChannelConnector。先看下它的类图:
可以看到它也实现了LifeCycle接口,并且对于一个监听请求的组件来说肯定是组件已启动就需要设置监听了,所以下面先看下doStart()方法。首先是SelectChannelConnector的:
@Override
protected void doStart() throws Exception
{
//设置最大的selector数量
_manager.setSelectSets(getAcceptors());
//设置selector上的最大超时等待实际
_manager.setMaxIdleTime(getMaxIdleTime());
//lowResourcesConnections: 连接数量达到该数值时,Jetty会认为服务器资源已被耗尽。
_manager.setLowResourcesConnections(getLowResourcesConnections());
//lowResourcesMaxIdleTime:表示可用线程稀少时或者当资源饱和时,连接最大等待时间,时间单位是毫秒,一般设置为<= maxIdleTime.
_manager.setLowResourcesMaxIdleTime(getLowResourcesMaxIdleTime());
super.doStart();
}
上面的代码主要是在操作_manager属性,给它设置不同的参数。这个_manager是一个SelectorManager对象,其实从名称中就可以看出来是用来管理selector的一个类。SelectorManegery继承了AbstractLifeCycle,并且也是抽象类,这里实际上是它的一个子类:ConnectorSelectorManager的实例。这里先不分析SelectorManager,先看下AbstractConnector的dostart()方法,主要的启动逻辑其实都在这个方法里。
@Override
protected void doStart() throws Exception
{
if (_server == null)
throw new IllegalStateException("No server");
// 打开端口进行监听
open();
if (_threadPool == null)
{ //如果线程池为空,从Server中取出设置好的线程池
_threadPool = _server.getThreadPool();
addBean(_threadPool,false);
}
//这一步是启动管理的其它组件,包括_manager
super.doStart();
// 启动Acceptor线程监听客户端请求
synchronized (this)
{
_acceptorThreads = new Thread[getAcceptors()];
for (int i = 0; i < _acceptorThreads.length; i++)
//在线程中给Acceptor分配执行线程
if (!_threadPool.dispatch(new Acceptor(i)))
throw new IllegalStateException("!accepting");
if (_threadPool.isLowOnThreads())
LOG.warn("insufficient threads configured for {}",this);
}
LOG.info("Started {}",this);
}
首先看下open()方法,这个方法负责创建一个用于监听的ServletSocketChannel对象,也就是_acceptChannel属性。
public void open() throws IOException
{
synchronized(this)
{
if (_acceptChannel == null)
{
// 创建一个新的ServerSocketChannel对象
_acceptChannel = ServerSocketChannel.open();
//设置为非阻塞模式
_acceptChannel.configureBlocking(true);
//给ServerSocket设定端口和IP _acceptChannel.socket().setReuseAddress(getReuseAddress());
InetSocketAddress addr = getHost()==null?new InetSocketAddress(getPort()):new InetSocketAddress(getHost(),getPort());
//这里的第二个参数表示最大请求长度 _acceptChannel.socket().bind(addr,getAcceptQueueSize());
_localPort=_acceptChannel.socket().getLocalPort();
if (_localPort<=0)
throw new IOException("Server channel not bound");
addBean(_acceptChannel);
}
}
}
设置好ServerSocketChannel之后就会启动Acceptor线程了,这里的Acceptor的数目是可以在配置文件中配置的,如果不进行配置,则会默认按下面的公式配置,Runtime.availableProcessors() 方法返回的是Java虚拟机的可用的处理器数量
Math.max(1,(Runtime.getRuntime().availableProcessors()+3)/4)
Acceptor就是一个Runnable对象,所以将其交给_threadPool.dispatch()方法之后,实际上就是为其启动一个线程处理它里面run()方法定义的逻辑。下面是其源码:
private class Acceptor implements Runnable
{
int _acceptor = 0;
Acceptor(int id)
{
_acceptor = id;
}
public void run()
{
Thread current = Thread.currentThread();
String name;
synchronized (AbstractConnector.this)
{
if (_acceptorThreads == null)
return;
_acceptorThreads[_acceptor] = current; //将_acceptorThreads数组中的第_acceptor号元素设置为当前线程
name = _acceptorThreads[_acceptor].getName();
current.setName(name