Анализ запросов Tomcat 7 (1) Генерация потоков обработки

Java задняя часть исходный код Tomcat

В конфигурации по умолчанию после запуска Tomcat вы увидите в общей сложности 6 потоков, работающих в фоновом режиме. Один из них — пользовательский поток, а остальные пять — потоки демона (как показано на рисунке ниже).

Если вы не знакомы с такими понятиями, как пользовательские потоки, потоки демона и т. д., обратитесь к предыдущей статье —Принцип отключения сервера Tomcat 7. Здесь основное внимание уделяется двум потокам демона, начинающимся с http-bio-8080 (т.е. http-bio-8080-Acceptor-0 и http-bio-8080-AsyncTimeout ), так как это происходит, когда мы публикуем веб-приложение с конфигурацией Tomcat по умолчанию. Поток, который фактически обрабатывает запрос. Давайте сначала посмотрим, как эти два потока генерируются и запускаются при запуске контейнера.

В предыдущей серии статей о запуске 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();
    }

Выше приведен процесс генерации и запуска двух фоновых потоков, описанных в начале этой статьи.Схема последовательности вызовов родственных классов показана на следующем рисунке:

Точно так же методы генерации и запуска двух потоков демона, ajp-bio-8009-Acceptor-0 и ajp-bio-8009-AsyncTimeout, также одинаковы и здесь повторяться не будут.