Connector组件 - 969251639/study GitHub Wiki
Connector时tomcat用来接受对外请求的一个组件,它可以根据protocol属性创建不同的Connector来处理不同协议的连接
<!-- 处理http请求的连接 -->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- 处理ajp请求的连接 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
我们用得最多基本都是http,所以下面就只分析tomcat如何处理http的请求
Connector创建时会先创建一个protocolHandler,而protocolHandler时一个抽象的概念,具体可以有http的protocolHandler,有ajp的protocolHandler等
public Connector(String protocol) {
setProtocol(protocol);
// Instantiate protocol handler
ProtocolHandler p = null;
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
p = (ProtocolHandler) clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
} finally {
this.protocolHandler = p;
}
if (!Globals.STRICT_SERVLET_COMPLIANCE) {
URIEncoding = "UTF-8";
URIEncodingLower = URIEncoding.toLowerCase(Locale.ENGLISH);
}
}
public void setProtocol(String protocol) {
if (AprLifecycleListener.isAprAvailable()) {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpAprProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
} else {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
}
} else {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11NioProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpNioProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
}
}
}
可以看到默认的http协议用的就是org.apache.coyote.http11.Http11NioProtocol(protocolHandler )
接下来看它的init方法
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// Initialize adapter
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
// Make sure parseBodyMethodsSet has a default
if( null == parseBodyMethodsSet ) {
setParseBodyMethods(getParseBodyMethods());
}
if (protocolHandler.isAprRequired() &&
!AprLifecycleListener.isAprAvailable()) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerNoApr",
getProtocolHandlerClassName()));
}
try {
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException
(sm.getString
("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
}
主要做两件事
- 创建一个适配器,用来适配原生的request和response到HttpServletRequest和HttpServletResponse
- 初始化protocolHandler
先看适配器,使用servlet的时候的入口是service方法
public interface Servlet {
...
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
...
}
所以适配器也肯定会适配该方法,也就是CoyoteAdapter中的service方法
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res)
throws Exception {
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
//request和response是Servlet真正需要的HttpServletRequest和HttpServletResponse,需要将
//org.apache.coyote.Request和org.apache.coyote.Response转成HttpServletRequest和HttpServletResponse
if (request == null) {//创建HttpServletRequest和创建HttpServletResponse
// Create objects
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
// Set query string encoding
req.getParameters().setQueryStringEncoding
(connector.getURIEncoding());//处理请求组编码
}
...
// Parse and set Catalina and configuration specific
// request parameters
req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());//设置该请求的线程名
postParseSuccess = postParseRequest(req, request, res, response);//解析http
if (postParseSuccess) {
//check valves if we support async
request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
// Calling the container
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
...
}
...
}
这个方法很长,最主要的是**connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);**这行代码的调用,这是一个tomcat管道的调用,用来组织tomcat内部各个组件的数据输入输出
再看tomcat架构图其实也是可以发现,内部的组件是有箭头串联起来的,而这些箭头就是管道(pipeline)
管道从connector开始流入(connector时连接的起点),最后流入到Wrapper未终点,而Wrapper其实就是我们想要的Servlet
这里只需要先知道数据从这里开始流入即可,先跳过管道的调用,再看回Connector的start方法
https://github.com/969251639/study/wiki/%E7%AE%A1%E9%81%93%E7%BB%84%E4%BB%B6
@Override
protected void startInternal() throws LifecycleException {
// Validate settings before starting
if (getPort() < 0) {
throw new LifecycleException(sm.getString(
"coyoteConnector.invalidPort", Integer.valueOf(getPort())));
}
setState(LifecycleState.STARTING);
try {
protocolHandler.start();
} catch (Exception e) {
String errPrefix = "";
if(this.service != null) {
errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
}
throw new LifecycleException
(errPrefix + " " + sm.getString
("coyoteConnector.protocolHandlerStartFailed"), e);
}
}
connector的启动其实就是protocolHandler的启动,看org.apache.coyote.http11.Http11NioProtocol的启动过程(托管它的父类AbstractProtocol进行启动)
@Override
public void start() throws Exception {
if (getLog().isInfoEnabled())
getLog().info(sm.getString("abstractProtocolHandler.start",
getName()));
try {
endpoint.start();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.startError",
getName()), ex);
throw ex;
}
}
会发现启动也很简单,就是启用了endpoint,那么endpoint又是干嘛的呢?
首先先跳回协议处理器org.apache.coyote.http11.Http11NioProtocol(protocolHandler )的构造方法
public Http11NioProtocol() {
endpoint=new NioEndpoint();
cHandler = new Http11ConnectionHandler(this);
((NioEndpoint) endpoint).setHandler(cHandler);
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
在创建Http11NioProtocol对象时就已经创建好了endpoint组件,同时也给endpoint组件设置了一个Http11ConnectionHandler,用来处理socket操作
然后看endpoint的start方法,这个方法在NioEndpoint的父类(AbstractEndpoint)中实现
public final void start() throws Exception {
if (bindState == BindState.UNBOUND) {
bind();
bindState = BindState.BOUND_ON_START;
}
startInternal();
}
首先绑定端口号,然后启动真正的NioEndpoint组件
先看绑定操作
@Override
public void bind() throws Exception {
//绑定地址和端口
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
serverSock.socket().bind(addr,getBacklog());
serverSock.configureBlocking(true); //mimic APR behavior
serverSock.socket().setSoTimeout(getSocketProperties().getSoTimeout());
// Initialize thread count defaults for acceptor, poller
if (acceptorThreadCount == 0) {//设置接受请求的线程数,一般为1个
// FIXME: Doesn't seem to work that well with multiple accept threads
acceptorThreadCount = 1;
}
if (pollerThreadCount <= 0) {//设置poller线程数,一般未cpu的核数, private int pollerThreadCount = Math.min(2,Runtime.getRuntime().availableProcessors());
//minimum one poller thread
pollerThreadCount = 1;
}
stopLatch = new CountDownLatch(pollerThreadCount);
// SSL
if (isSSLEnabled()) {
...
}
...
}
最后看Connector的启动过程
@Override
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
//对象缓冲
processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getProcessorCache());
eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getEventCache());
nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getBufferPool());
// Create worker collection
if ( getExecutor() == null ) {//创建worker线程池
createExecutor();
}
initializeConnectionLatch();//初始化连接数控制器组件
// Start poller threads
pollers = new Poller[getPollerThreadCount()];
for (int i=0; i<pollers.length; i++) {//创建poller线程池
pollers[i] = new Poller();
Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
}
startAcceptorThreads();//创建acceptor线程池
}
}
可以看到,endpoint的组件就是把poller线程,acceptor线程,worker线程给跑起来,最后形成一个典型的reactor多线程模型的线程模型
最后总结下整个过程,然后逐个分析里面的每一个组件
-
因为connector是在service中组件中,所以启动service组件之后也会启动connector组件
-
初始化connector时主要根据server.xml的配置设置协议并创建协议处理器,然后在生命周期初始化创建了一个协议适配器( Coyote )
-
启动协议处理器(根据不同的协议创建不同的协议处理器,默认Http11NioProtocol )
-
启动协议处理器只做一件事,把endpoint组件给启动了,endpoint在创建协议处理器时就已经创建了,不同的协议处理器有不同的endpoint,这里的Http11NioProtocol对应的endpoint是NioEndpoint,同时也创建了一个http连接处理器(Http11ConnectionHandler),启动endpoint过程中先绑定端口号,最后执行endpoint的生命周期
-
创建工作线程池(最小MinSpareThreads,最大MaxThreads),创建最大连接数的门栅,创建poller线程(线程数根据cpu核数,最小为2),创建acceptor线程(默认为1个)
以上连接器所有组件已全部启动完成,接下来就是等待客户端请求连接和处理
下面是对每个组件的具体分析:
LimitLatch:连接数控制器
Acceptor:接收对外请求
Poller:对连接通道的读与写
WorkerThreadPool:工作线程池,真正执行业务逻辑的地方
Pipeline:管道,连接内部组件的数据输入