В конфигурации по умолчанию после запуска Tomcat вы увидите в общей сложности 6 потоков, работающих в фоновом режиме. Один из них — пользовательский поток, а остальные пять — потоки демона (как показано на рисунке ниже).
В предыдущей серии статей о запуске Tomcat мы видели, что при запуске контейнера Tomcat он будет использовать Digester для чтения файла server.xml для создания соответствующих объектов компонентов и вызывать их методы init и start в цепочке вызовов. , и прочитайте сервер в Digester. Вот как обрабатывается узел соединителя в .xml:
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector",
new SetAllPropertiesRule(new String[]{"executor"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");
См. выше кодorg.apache.catalina.startup.CatalinaСтроки с 366 по 372 класса. Таким образом, при обнаружении узла Server/Service/Connector в файле server.xml он инициирует вызов метода begin класса ConnectorCreateRule:
1 public void begin(String namespace, String name, Attributes attributes)
2 throws Exception {
3 Service svc = (Service)digester.peek();
4 Executor ex = null;
5 if ( attributes.getValue("executor")!=null ) {
6 ex = svc.getExecutor(attributes.getValue("executor"));
7 }
8 Connector con = new Connector(attributes.getValue("protocol"));
9 if ( ex != null ) _setExecutor(con,ex);
10
11 digester.push(con);
12 }
В строке 8 он вызывается на основе атрибута протокола узла Сервер/Сервис/Соединитель в файле конфигурации.org.apache.catalina.connector.ConnectorКонструктор класса и по умолчанию узел Сервер/Сервис/Соединитель в файле server.xml имеют две конфигурации:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
Давайте сначала посмотрим на первый узел Connector.При вызове конструктора Connector будет передана строка HTTP/1.1.
1 public Connector(String protocol) {
2 setProtocol(protocol);
3 // Instantiate protocol handler
4 try {
5 Class<?> clazz = Class.forName(protocolHandlerClassName);
6 this.protocolHandler = (ProtocolHandler) clazz.newInstance();
7 } catch (Exception e) {
8 log.error(sm.getString(
9 "coyoteConnector.protocolHandlerInstantiationFailed"), e);
10 }
11 }
Здесь будет выполнен первыйorg.apache.catalina.connector.ConnectorМетод setProtocol класса:
1 public void setProtocol(String protocol) {
2
3 if (AprLifecycleListener.isAprAvailable()) {
4 if ("HTTP/1.1".equals(protocol)) {
5 setProtocolHandlerClassName
6 ("org.apache.coyote.http11.Http11AprProtocol");
7 } else if ("AJP/1.3".equals(protocol)) {
8 setProtocolHandlerClassName
9 ("org.apache.coyote.ajp.AjpAprProtocol");
10 } else if (protocol != null) {
11 setProtocolHandlerClassName(protocol);
12 } else {
13 setProtocolHandlerClassName
14 ("org.apache.coyote.http11.Http11AprProtocol");
15 }
16 } else {
17 if ("HTTP/1.1".equals(protocol)) {
18 setProtocolHandlerClassName
19 ("org.apache.coyote.http11.Http11Protocol");
20 } else if ("AJP/1.3".equals(protocol)) {
21 setProtocolHandlerClassName
22 ("org.apache.coyote.ajp.AjpProtocol");
23 } else if (protocol != null) {
24 setProtocolHandlerClassName(protocol);
25 }
26 }
27
28 }
Так что на этот раз позвонитsetProtocolHandlerClassName("org.apache.coyote.http11.Http11Protocol")который задает для переменной экземпляра класса Connector значение protocolHandlerClassNameorg.apache.coyote.http11.Http11Protocol, а затем в конструкторе Коннектора будет сгенерирована переменная protocolHandlerClassName в соответствии со значением переменнойorg.apache.coyote.http11.Http11Protocolобъект и назначьте объект переменной экземпляра protocolHandler класса Connector. В конструкторе класса Http11Protocolorg.apache.tomcat.util.net.JIoEndpointОбъект:
1 public Http11Protocol() {
2 endpoint = new JIoEndpoint();
3 cHandler = new Http11ConnectionHandler(this);
4 ((JIoEndpoint) endpoint).setHandler(cHandler);
5 setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
6 setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
7 setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
8 }
Ниже показана схема последовательности вызовов методов построения нескольких связанных объектов, среди которыхorg.apache.coyote.AbstractProtocolдаorg.apache.coyote.http11.Http11Protocolродительский классorg.apache.tomcat.util.net.AbstractEndpointдаorg.apache.tomcat.util.net.JIoEndpointродительский класс.
org.apache.catalina.connector.ConnectorВ это время будет вызван метод запуска Tomcat, описанный в предыдущем анализе запуска Tomcat.org.apache.catalina.connector.ConnectorМетод startInternal класса:
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);
}
mapperListener.start();
}
В строке 12 вызывается начальный метод экземпляра переменной protocolHandler. При анализе конструктора класса Connector выше было обнаружено, что значение переменной protocolHandler равноorg.apache.coyote.http11.Http11Protocolобъект, поэтому в это время будет вызываться метод запуска класса. Метод запуска не определен в классе Http11Protocol, и здесь будет вызываться его родительский классorg.apache.coyote.AbstractProtocolметод запуска в:
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;
}
}
Это вызовет начальный метод объекта конечной точки, а конечная точкаorg.apache.tomcat.util.net.JIoEndpointЭкземпляр класса (упомянутого выше при разговоре о конструкторе класса Http11Protocol), где в итоге будет выполнен метод startInternal класса:
1 @Override
2 public void startInternal() throws Exception {
3
4 if (!running) {
5 running = true;
6 paused = false;
7
8 // Create worker collection
9 if (getExecutor() == null) {
10 createExecutor();
11 }
12
13 initializeConnectionLatch();
14
15 startAcceptorThreads();
16
17 // Start async timeout thread
18 Thread timeoutThread = new Thread(new AsyncTimeout(),
19 getName() + "-AsyncTimeout");
20 timeoutThread.setPriority(threadPriority);
21 timeoutThread.setDaemon(true);
22 timeoutThread.start();
23 }
24 }
Именно здесь генерируются и запускаются два потока http-bio-8080-Acceptor-0 и http-bio-8080-AsyncTimeout, упомянутые в начале этой статьи. Строки с 17 по 22 предназначены для создания и запуска потока http-bio-8080-AsyncTimeout, а в строке 15 здесь вызывается родительский класс.org.apache.tomcat.util.net.AbstractEndpointМетод startAcceptorThreads:
1 protected final void startAcceptorThreads() {
2 int count = getAcceptorThreadCount();
3 acceptors = new Acceptor[count];
4
5 for (int i = 0; i < count; i++) {
6 acceptors[i] = createAcceptor();
7 String threadName = getName() + "-Acceptor-" + i;
8 acceptors[i].setThreadName(threadName);
9 Thread t = new Thread(acceptors[i], threadName);
10 t.setPriority(getAcceptorThreadPriority());
11 t.setDaemon(getDaemon());
12 t.start();
13 }
14 }
15
16
17 /**
18 * Hook to allow Endpoints to provide a specific Acceptor implementation.
19 */
20 protected abstract Acceptor createAcceptor();
Здесь будет создан и запущен поток http-bio-8080-Acceptor-0. Обратите внимание, что при построении потока в строке 6 будет вызываться абстрактный метод в строке 20, реализованный в классе JIoEndpoint:
@Override
protected AbstractEndpoint.Acceptor createAcceptor() {
return new Acceptor();
}
Выше приведен процесс генерации и запуска двух фоновых потоков, описанных в начале этой статьи.Схема последовательности вызовов родственных классов показана на следующем рисунке: