tomcat с самого начала для интеграции Servlet Knew

Java

причина

Это также из-за моей собственной неосмотрительности до того, как я написалJava编程方法论-Reactor与WebfluxВ то время, поскольку я чувствовал, что у tomcat уже есть хороший пост в блоге о части коннектора, я сделал ссылку на него и не проводил углубленного анализа исходного кода.В результате, когда я записал и поделился видео, я обнаружил, что содержание моей статьи не соответствует исходному коду. Я искал некоторые статьи блога на китайском языке через поисковую систему, но они не были удовлетворительными. Просто я использовал последний исходный код, чтобы реорганизовать часть контента. о разъеме tomcat. Адрес источника справки:GitHub.com/Apache/Том С…

оJava编程方法论-Reactor与WebfluxСовместное использование видео Rxjava и Reactor завершено. Адрес станции b выглядит следующим образом:

Интерпретация исходного кода Rxjava и совместное использование:вооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооо

Интерпретация и совместное использование исходного кода Reactor:вооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооо

Подробное объяснение процесса запуска Tomcat

Запуск и завершение основных операций Tomcat

В системах Linux запускайте и выключайте Tomcat с помощью командных операций.

Войдите в каталог bin под Tomcat:

cd /java/tomcat/bin

Запустите команду Tomcat:

./startup.sh

Остановите команду службы Tomcat:

./shutdown.sh

Казнить кота./shutdown.shПосле этого, хотя служба tomcat не может быть доступна нормально, ноps -ef | grep tomcatпозже нашелtomcatсоответствующийjavaПроцесс не уничтожается при закрытии веб-контейнера, и есть зомбиjavaпроцесс. Я читал в Интернете, что причиной зомби-процесса может быть то, что есть потоки, не являющиеся демонами (т.е. пользовательские потоки), и JVM не выйдет (когда все потоки в JVM являются потоками демона, JVM может выйти; если есть другой или более высокий поток, не являющийся демоном, JVM не завершится). Проверьте, завершается ли процесс Tomcat следующей командой:

ps -ef|grep tomcat

Если есть пользовательский поток, просто убейте его и используйте.kill -9 pid

Bootstrap подробное объяснение процесса запуска

Затем мы идем отstartup.shВ этом сценарии оболочки можно найти, что он, наконец, вызываетcatalina.sh start, поэтому находимcatalina.shв, вelif [ "$1" = "start" ] ;, спускаемся вниз и обнаруживаем, что он звонитorg.apache.catalina.startup.Bootstrap.javaв этой категорииstart()метод:

/**
* org.apache.catalina.startup.Bootstrap
* Start the Catalina daemon.
* @throws Exception Fatal start error
*/
public void start()
    throws Exception {
    if( catalinaDaemon==null ) init();

    Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
    method.invoke(catalinaDaemon, (Object [])null);

}

Здесь, когда сервер запускается в первый раз, егоinit(), который в основном используется для созданияorg.apache.catalina.startup.Catalina.javaэкземпляр класса:

/**
* org.apache.catalina.startup.Bootstrap
* Initialize daemon.
* @throws Exception Fatal initialization error
*/
public void init() throws Exception {

    initClassLoaders();

    Thread.currentThread().setContextClassLoader(catalinaLoader);

    SecurityClassLoad.securityClassLoad(catalinaLoader);

    // Load our startup class and call its process() method
    if (log.isDebugEnabled())
        log.debug("Loading startup class");
    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    Object startupInstance = startupClass.getConstructor().newInstance();

    // Set the shared extensions class loader
    if (log.isDebugEnabled())
        log.debug("Setting startup class properties");
    String methodName = "setParentClassLoader";
    Class<?> paramTypes[] = new Class[1];
    paramTypes[0] = Class.forName("java.lang.ClassLoader");
    Object paramValues[] = new Object[1];
    paramValues[0] = sharedLoader;
    Method method =
        startupInstance.getClass().getMethod(methodName, paramTypes);
    method.invoke(startupInstance, paramValues);

    catalinaDaemon = startupInstance;

}

Подробное объяснение процесса запуска Catalina

Интерпретация начала в Catalina

Затем метод запуска экземпляра Catalina вызывается в методе start() Bootstrap:

/**
* org.apache.catalina.startup.Catalina
* Start a new server instance.
*/
public void start() {

    if (getServer() == null) {
        load();
    }

    if (getServer() == null) {
        log.fatal(sm.getString("catalina.noServer"));
        return;
    }

    long t1 = System.nanoTime();

    // Start the new server
    try {
        getServer().start();
    } catch (LifecycleException e) {
        log.fatal(sm.getString("catalina.serverStartFail"), e);
        try {
            getServer().destroy();
        } catch (LifecycleException e1) {
            log.debug("destroy() failed for failed Server ", e1);
        }
        return;
    }

    long t2 = System.nanoTime();
    if(log.isInfoEnabled()) {
        log.info(sm.getString("catalina.startup", Long.valueOf((t2 - t1) / 1000000)));
    }

    // Register shutdown hook
    if (useShutdownHook) {
        if (shutdownHook == null) {
            shutdownHook = new CatalinaShutdownHook();
        }
        Runtime.getRuntime().addShutdownHook(shutdownHook);

        // If JULI is being used, disable JULI's shutdown hook since
        // shutdown hooks run in parallel and log messages may be lost
        // if JULI's hook completes before the CatalinaShutdownHook()
        LogManager logManager = LogManager.getLogManager();
        if (logManager instanceof ClassLoaderLogManager) {
            ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                    false);
        }
    }

    if (await) {
        await();
        stop();
    }
}

Здесь нас в основном интересуетload(),getServer().start(), для последнего мы видим расчет времени запуска до и после него, который также является распечаткой журнала, которую мы обычно видим в процессе запуска tomcat, и я не буду упоминать его здесь.

Интерпретация load() в Catalina

Сначала давайте посмотрим на load(), здесь он пройдетcreateStartDigester()Создайте и настройте Digester, который мы начнем, затем получите файл serverXML, который мы настроим, настройте внутреннюю часть свойства, наконец, вызовитеgetServer().init():

/**
* org.apache.catalina.startup.Catalina
* Start a new server instance.
*/
public void load() {

    if (loaded) {
        return;
    }
    loaded = true;

    long t1 = System.nanoTime();

    initDirs();

    // Before digester - it may be needed
    initNaming();

    // Set configuration source
    ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
    File file = configFile();

    // Create and execute our Digester
    Digester digester = createStartDigester();

    try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
        InputStream inputStream = resource.getInputStream();
        InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
        inputSource.setByteStream(inputStream);
        digester.push(this);
        digester.parse(inputSource);
    } catch (Exception e) {
        if  (file == null) {
            log.warn(sm.getString("catalina.configFail", getConfigFile() + "] or [server-embed.xml"), e);
        } else {
            log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
            if (file.exists() && !file.canRead()) {
                log.warn(sm.getString("catalina.incorrectPermissions"));
            }
        }
        return;
    }

    getServer().setCatalina(this);
    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

    // Stream redirection
    initStreams();

    // Start the new server
    try {
        getServer().init();
    } catch (LifecycleException e) {
        if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
            throw new java.lang.Error(e);
        } else {
            log.error(sm.getString("catalina.initError"), e);
        }
    }

    long t2 = System.nanoTime();
    if(log.isInfoEnabled()) {
        log.info(sm.getString("catalina.init", Long.valueOf((t2 - t1) / 1000000)));
    }
}

Вот откуда этот сервер, мы изdigester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");Как видите, он использует экземпляр этого класса, вернемся кdigester.push(this); digester.parse(inputSource);Когда появляются эти две строки кода, видно, что Digester.push(this) вызывается перед синтаксическим анализом. В это время верхний элемент стека — Catalina, который используется для настройки сервера для каталины. Здесь , надо исправитьdigesterАнализ включает в себя следующее:

как проанализировано<Server>будет создан, когдаStandardServerЭкземпляр класса и вызывается отражениемDigesterизstackвершина объекта стекаsetterметод (вызванный метод передается вnameстоимость определяется).digesterучаствует вIntrospectionUtils.setProperty(top, name, value)метод, то естьtopявляется верхним объектом стека,nameИмя свойства, которое нужно установить для этого объекта вершины стека,valueзначение свойства, которое необходимо установить. В начале верхний элемент стекаCatalina, т.е. вызовCatalina.setServer(Server object)настройки методаServerбыть вызванным позжеServer.start()подготовься, а потомStandardServerэкземпляр объекта вDigesterизstackстек объектов.

getServer().init()

Далее посмотримgetServer().init(), сверху найдемorg.apache.catalina.core.StandardServer.javaэтот класс, который наследуетLifecycleMBeanBaseи понялServer,пройти черезLifecycleMBeanBaseЭтот класс, объясните этоStandardServerУправляемый жизненный цикл, т. е. черезLifecycleMBeanBaseотецLifecycleBaseосуществленныйinit()метод:

//org.apache.catalina.util.LifecycleBase.java

@Override
public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }

    try {
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        initInternal();
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
        handleSubClassException(t, "lifecycleBase.initFail", toString());
    }
}

Поэтому мы обращаем внимание наinitInternal()существуетStandardServerРеализация в , слишком много кода, вот процесс: 1. Вызовите метод родительского класса org.apache.catalina.util.LifecycleMBeanBase#initInternal, чтобы зарегистрировать MBean.

2. Зарегистрируйте MBean других атрибутов этого класса

3. Инициализация NamingResources: globalNamingResources.init();

4. Найдите от обычного ClassLoader до SystemClassLoader, пройдите путь просмотра, соответствующий каждому classLoader, найдите файл в конце jar, прочитайте информацию манифеста и добавьте его в свойство ExtensionValidator#containerManifestResources.

5. Инициализируйте службу, реализация по умолчанию — StandardService.

   i) вызвать метод super.initInternal()

ii) Инициализация контейнера, где экземпляром контейнера является StandardEngine. ​
iii) Инициализация исполнителя ​
iv) Инициализация коннектора: ​
​ a) org.apache.catalina.connector.Connector Connector[HTTP/1.1-8080] ​
b) org.apache.catalina.connector.Connector Connector[AJP/1.3-8009]

Интерпретация getServer().start() при запуске в Catalina

Здесь мы можем видетьStandardServerродительский классorg.apache.catalina.util.LifecycleBase.javaвыполнение:

@Override
public final synchronized void start() throws LifecycleException {

    if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
            LifecycleState.STARTED.equals(state)) {

        if (log.isDebugEnabled()) {
            Exception e = new LifecycleException();
            log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
        } else if (log.isInfoEnabled()) {
            log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
        }

        return;
    }

    if (state.equals(LifecycleState.NEW)) {
        init();
    } else if (state.equals(LifecycleState.FAILED)) {
        stop();
    } else if (!state.equals(LifecycleState.INITIALIZED) &&
            !state.equals(LifecycleState.STOPPED)) {
        invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }

    try {
        setStateInternal(LifecycleState.STARTING_PREP, null, false);
        startInternal();
        if (state.equals(LifecycleState.FAILED)) {
            // This is a 'controlled' failure. The component put itself into the
            // FAILED state so call stop() to complete the clean-up.
            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
        // This is an 'uncontrolled' failure so put the component into the
        // FAILED state and throw an exception.
        handleSubClassException(t, "lifecycleBase.startFail", toString());
    }
}

заStandardServer, мы ориентируемся на егоstartInternal();Реализация исходного кода не выложена, конкретный процесс выглядит следующим образом: 1. Запустите событие CONFIGURE_START_EVENT.

2. Установите статус этого объекта на ЗАПУСК

3. Запуск NameingResource: globalNamingResources.start(); 4. Запускается Стандартная служба.

   i) Установите статус НАЧАЛО

ii) Контейнер запускается, то есть запускается StandardEngine ​
iii) Исполнитель начинает ​
iv) Начало соединителя: ​
​ a) org.apache.catalina.connector.Connector Connector[HTTP/1.1-8080] ​
b) org.apache.catalina.connector.Connector Connector[AJP/1.3-8009]

Наконец, мы изучили главного героя, о котором я хочу поговорить.Connector.

Интерпретация соединителя

Конструктор коннекторов

нам даноapache-tomcat-9.0.14\confВ server.xml в каталоге (пожалуйста, загрузите соответствующую версию tomcat здесь)ConnectorКонфигурация показывает, что протокол конфигурации порта 8080 по умолчаниюHTTP/1.1.

<Connector port="8080" protocol="HTTP/1.1"
            connectionTimeout="20000"
            redirectPort="8443" />
            <!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Зная это, посмотрим на реализацию в его коде:

public Connector() {
    this("org.apache.coyote.http11.Http11NioProtocol");
}


public Connector(String protocol) {
    boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
            AprLifecycleListener.getUseAprConnector();

    if ("HTTP/1.1".equals(protocol) || protocol == null) {
        if (aprConnector) {
            protocolHandlerClassName = "org.apache.coyote.http11.Http11AprProtocol";
        } else {
            protocolHandlerClassName = "org.apache.coyote.http11.Http11NioProtocol";
        }
    } else if ("AJP/1.3".equals(protocol)) {
        if (aprConnector) {
            protocolHandlerClassName = "org.apache.coyote.ajp.AjpAprProtocol";
        } else {
            protocolHandlerClassName = "org.apache.coyote.ajp.AjpNioProtocol";
        }
    } else {
        protocolHandlerClassName = protocol;
    }

    // Instantiate protocol handler
    ProtocolHandler p = null;
    try {
        Class<?> clazz = Class.forName(protocolHandlerClassName);
        p = (ProtocolHandler) clazz.getConstructor().newInstance();
    } catch (Exception e) {
        log.error(sm.getString(
                "coyoteConnector.protocolHandlerInstantiationFailed"), e);
    } finally {
        this.protocolHandler = p;
    }

    // Default for Connector depends on this system property
    setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
}

Для tomcat8.5 и выше по умолчанию используетсяHttp11NioProtocolпротокол, вот, мы установили егоHTTP/1.1, но по суждению приведенного выше оператора if оно равно, т. е. окончательно выбраноHttp11NioProtocol.

Инициализация и запуск коннектора

Аналогично, как мы видим из предыдущего раздела, мы задействуем инициализацию Коннектора, то есть он также будет наследоватьLifecycleMBeanBase, тогда давайте посмотрим на соответствующиеinitInternal()выполнить:

@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    if (protocolHandler == null) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));
    }

    // Initialize adapter
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);
    if (service != null) {
        protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
    }

    // Make sure parseBodyMethodsSet has a default
    if (null == parseBodyMethodsSet) {
        setParseBodyMethods(getParseBodyMethods());
    }

    if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
        throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoApr",
                getProtocolHandlerClassName()));
    }
    if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&
            protocolHandler instanceof AbstractHttp11JsseProtocol) {
        AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
                (AbstractHttp11JsseProtocol<?>) protocolHandler;
        if (jsseProtocolHandler.isSSLEnabled() &&
                jsseProtocolHandler.getSslImplementationName() == null) {
            // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
            jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
        }
    }

    try {
        protocolHandler.init();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
    }
}

Здесь задействован следующий процесс: 1. Зарегистрируйте MBean

2. COYOTEADAPTER создается, а CoyoTeadapter является точкой входа в запрос. Когда есть запрос, койотедаптер обрабатывает состояние, перерабатывает запрос в конце и передает промежуточный процесс к трубопроводу.

3. Инициализация ProtocolHandler (org.apache.coyote.http11.Http11Protocol)

На этом этапе инициализация конечной точки завершена.

Не говоря уже о запуске, он устанавливает состояние этого объекта в ЗАПУСК и одновременно вызываетprotocolHandler.start();, а затем пришло время войти в наш основной ритм.

@Override
protected void startInternal() throws LifecycleException {

    // Validate settings before starting
    if (getPortWithOffset() < 0) {
        throw new LifecycleException(sm.getString(
                "coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
    }

    setState(LifecycleState.STARTING);

    try {
        protocolHandler.start();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
    }
}

Толкование протокола

Здесь мы реализуем непосредственно из его абстракцииorg.apache.coyote.AbstractProtocol.javaС точки зрения, он также следует жизненному циклу, поэтому он также наследуетLifecycleMBeanBaseи реализовывать своиinit()иstart()и другие методы жизненного цикла, которые внутренне реализуются соответствующей самореализациейendpointдля выполнения определенной логики:

//org.apache.coyote.AbstractProtocol.java
@Override
public void init() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
        logPortOffset();
    }

    if (oname == null) {
        // Component not pre-registered so register it
        oname = createObjectName();
        if (oname != null) {
            Registry.getRegistry(null, null).registerComponent(this, oname, null);
        }
    }

    if (this.domain != null) {
        rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
        Registry.getRegistry(null, null).registerComponent(
                getHandler().getGlobal(), rgOname, null);
    }

    String endpointName = getName();
    endpoint.setName(endpointName.substring(1, endpointName.length()-1));
    endpoint.setDomain(domain);

    endpoint.init();
}


@Override
public void start() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
        logPortOffset();
    }

    endpoint.start();
    monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
            new Runnable() {
                @Override
                public void run() {
                    if (!isPaused()) {
                        startAsyncTimeout();
                    }
                }
            }, 0, 60, TimeUnit.SECONDS);
}

братьorg.apache.coyote.http11.Http11NioProtocolДля этого класса он получаетNioEndpointДостичь конструктора, воплощенного методом, при котором внутренняя часть банкиNioEndpointреализовать свою логику:

public class Http11NioProtocol extends AbstractHttp11JsseProtocol<NioChannel> {

    private static final Log log = LogFactory.getLog(Http11NioProtocol.class);


    public Http11NioProtocol() {
        super(new NioEndpoint());
    }


    @Override
    protected Log getLog() { return log; }


    // -------------------- Pool setup --------------------

    public void setPollerThreadCount(int count) {
        ((NioEndpoint)getEndpoint()).setPollerThreadCount(count);
    }

    public int getPollerThreadCount() {
        return ((NioEndpoint)getEndpoint()).getPollerThreadCount();
    }

    public void setSelectorTimeout(long timeout) {
        ((NioEndpoint)getEndpoint()).setSelectorTimeout(timeout);
    }

    public long getSelectorTimeout() {
        return ((NioEndpoint)getEndpoint()).getSelectorTimeout();
    }

    public void setPollerThreadPriority(int threadPriority) {
        ((NioEndpoint)getEndpoint()).setPollerThreadPriority(threadPriority);
    }

    public int getPollerThreadPriority() {
      return ((NioEndpoint)getEndpoint()).getPollerThreadPriority();
    }


    // ----------------------------------------------------- JMX related methods

    @Override
    protected String getNamePrefix() {
        if (isSSLEnabled()) {
            return "https-" + getSslImplementationShortName()+ "-nio";
        } else {
            return "http-nio";
        }
    }
}

Интерпретация конечной точки

здесь,EndPointОн используется для обработки определенных соединений и передачи данных, то есть для реализации сетевого соединения и управления, это внешнийI/OОперативная точка доступа. Основная задача – управлять внешнимsocketсоединение, и установит хорошееsocketСоединение передается соответствующему рабочему потоку. Два основных класса атрибутовAcceptorиPoller,SocketProcessor. мы начинаем сNioEndpointНапример, конкретный процесс его внутренней обработки запроса выглядит следующим образом:

В сочетании с последним разделом мы в основном сосредоточимся на егоProtocolКонкретные реализации методов жизненного цикла:

//org.apache.tomcat.util.net.AbstractEndpoint.java
public final void init() throws Exception {
    if (bindOnInit) {
        bindWithCleanup();
        bindState = BindState.BOUND_ON_INIT;
    }
    if (this.domain != null) {
        // Register endpoint (as ThreadPool - historical name)
        oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
        Registry.getRegistry(null, null).registerComponent(this, oname, null);

        ObjectName socketPropertiesOname = new ObjectName(domain +
                ":type=ThreadPool,name=\"" + getName() + "\",subType=SocketProperties");
        socketProperties.setObjectName(socketPropertiesOname);
        Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);

        for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
            registerJmx(sslHostConfig);
        }
    }
}

public final void start() throws Exception {
    if (bindState == BindState.UNBOUND) {
        bindWithCleanup();
        bindState = BindState.BOUND_ON_START;
    }
    startInternal();
}

//org.apache.tomcat.util.net.AbstractEndpoint.java
private void bindWithCleanup() throws Exception {
    try {
        bind();
    } catch (Throwable t) {
        // Ensure open sockets etc. are cleaned up if something goes
        // wrong during bind
        ExceptionUtils.handleThrowable(t);
        unbind();
        throw t;
    }
}

Эти два метода в основном вызываютbind(можно найти здесьbindWithCleanup()конкретная реализация) иstartlntemalметоды, это шаблонные методы, которые могут быть реализованы в соответствии с вашими потребностями.Здесь мы ссылаемся наNioEndpointреализация вbindКод метода следующий:

//org.apache.tomcat.util.net.NioEndpoint.java
@Override
public void bind() throws Exception {
    initServerSocket();

    // Initialize thread count defaults for acceptor, poller
    if (acceptorThreadCount == 0) {
        // FIXME: Doesn't seem to work that well with multiple accept threads
        acceptorThreadCount = 1;
    }
    if (pollerThreadCount <= 0) {
        //minimum one poller thread
        pollerThreadCount = 1;
    }
    setStopLatch(new CountDownLatch(pollerThreadCount));

    // Initialize SSL if needed
    initialiseSsl();

    selectorPool.open();
}

Метод привязки здесь сначала инициализируетсяServerSocket(Все мы касались этой штуки в сетевом программировании jdk, так что больше говорить не буду, вот инкапсулированный класс инструмента, реализацию смотрите ниже), а потом проверил представительAcceptorиPollerколичество потоков для инициализацииacceptorThreadCountсвойства иpollerThreadCountсвойства, которые имеют значение не менее 1.

// Separated out to make it easier for folks that extend NioEndpoint to
// implement custom [server]sockets
protected void initServerSocket() throws Exception {
    if (!getUseInheritedChannel()) {
        serverSock = ServerSocketChannel.open();
        socketProperties.setProperties(serverSock.socket());
        InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
        serverSock.socket().bind(addr,getAcceptCount());
    } else {
        // Retrieve the channel provided by the OS
        Channel ic = System.inheritedChannel();
        if (ic instanceof ServerSocketChannel) {
            serverSock = (ServerSocketChannel) ic;
        }
        if (serverSock == null) {
            throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
        }
    }
    serverSock.configureBlocking(true); //mimic APR behavior
}

здесь,AcceptorИспользуется для приема запросов, передачи полученных запросов вPollerобработки, все они запущены потоками для обработки. Также инициализированSSLи так далее.NioEndpointизstartInternalКод метода следующий:

/**
* The socket pollers.
*/
private Poller[] pollers = null;

/**
* Start the NIO endpoint, creating acceptor, poller threads.
*/
@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 ) {
            createExecutor();
        }

        initializeConnectionLatch();

        // Start poller threads
        pollers = new Poller[getPollerThreadCount()];
        for (int i=0; i<pollers.length; i++) {
            pollers[i] = new Poller();
            Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
            pollerThread.setPriority(threadPriority);
            pollerThread.setDaemon(true);
            pollerThread.start();
        }

        startAcceptorThreads();
    }
}

Здесь некоторые свойства инициализируются первыми, среди инициализированных свойствprocessorCacheдаSynchronizedStack<SocketProcessor>тип,SocketProcessorдаNioEndpointвнутренний класс ,PollerПосле получения запроса будет передано к нему,SocketProcessorпередаст запрос наHandler. затем началосьPollerиAcceptorЧтобы обработать запрос, мы должны обратить внимание на то,pollersпредставляет собой массив, который управляет кучейRunnable, как видно спереди, если мы не установим его, оно равно 1, то есть по умолчанию это только один поток. После того, как этот поток создан, он устанавливается как поток демона до тех пор, пока контейнер tomcat не закончится, что, естественно, закончится. Здесь, если мы хотим настроить его, мы можемserver.xmlСделайте соответствующие настройки в:

<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"  
               connectionTimeout="20000"  
               maxHeaderCount="64"  
               maxParameterCount="64"  
               maxHttpHeaderSize="8192"  
               URIEncoding="UTF-8"  
               useBodyEncodingForURI="false"  
               maxThreads="128"  
               minSpareThreads="12"  
               acceptCount="1024"  
               connectionLinger="-1"  
               keepAliveTimeout="60"  
               maxKeepAliveRequests="32"  
               maxConnections="10000"  
               acceptorThreadCount="1"  
               pollerThreadCount="2"  
               selectorTimeout="1000"  
               useSendfile="true"  
               selectorPool.maxSelectors="128"  
               redirectPort="8443" />  

запускатьAcceptorизstartAcceptorThreadsметод вAbstractEndpoint, код выглядит следующим образом:

protected void startAcceptorThreads() {
    int count = getAcceptorThreadCount();
    acceptors = new ArrayList<>(count);

    for (int i = 0; i < count; i++) {
        Acceptor<U> acceptor = new Acceptor<>(this);
        String threadName = getName() + "-Acceptor-" + i;
        acceptor.setThreadName(threadName);
        acceptors.add(acceptor);
        Thread t = new Thread(acceptor, threadName);
        t.setPriority(getAcceptorThreadPriority());
        t.setDaemon(getDaemon());
        t.start();
    }
}

здесьgetAcceptorThreadCountМетод заключается в получении атрибута acceptorThreadCount, обработанного в методе init, и после его получения будет запущено соответствующее количество потоков Acceptor для получения запроса. По умолчанию тоже 1, а способ создания тредов такой же, как и у Poller, так что больше говорить не буду.

Здесь давайте взглянем на документацию webapps/docs/config/http.xml:

<attribute name="acceptorThreadCount" required="false">
    <p>The number of threads to be used to accept connections. Increase this
    value on a multi CPU machine, although you would never really need more
    than <code>2</code>. Also, with a lot of non keep alive connections, you
    might want to increase this value as well. Default value is
    <code>1</code>.</p>
</attribute>

<attribute name="pollerThreadCount" required="false">
    <p>(int)The number of threads to be used to run for the polling events.
    Default value is <code>1</code> per processor but not more than 2.<br/>
    When accepting a socket, the operating system holds a global lock. So the benefit of
    going above 2 threads diminishes rapidly. Having more than one thread is for
    system that need to accept connections very rapidly. However usually just
    increasing <code>acceptCount</code> will solve that problem.
    Increasing this value may also be beneficial when a large amount of send file
    operations are going on.
    </p>
</attribute>

Отсюда видно, чтоacceptorThreadCountИспользуется для установки количества потоков для приема соединений. Увеличьте это значение на многопроцессорных машинах, хотя вам, вероятно, не нужно больше 2. Вы можете увеличить это значение, даже если имеется много соединений, не поддерживающих активность. Его значение по умолчанию равно 1.pollerThreadCountКоличество потоков, используемых для выполнения событий опроса. Значение по умолчанию — 1 на процессор, но не более 2 (2 в приведенной выше оптимизированной конфигурации). При принятии сокета ОС будет удерживать глобальную блокировку. Таким образом, преимущество более чем двух потоков быстро уменьшается. Когда в системе есть несколько потоков этого типа, она может принимать соединения очень быстро. Таким образом, увеличение acceptCount может решить эту проблему. Увеличение этого значения также может быть полезным, когда выполняется большое количество операций по отправке файлов.

Как работают Acceptor и Poller

Давайте сначала посмотрим на диаграмму последовательности, обработанную NioEndpoint:

Как работает акцептор

Как мы видим из вышеизложенного, и Acceptor, и Poller реализуют интерфейс Runnable, поэтому их основной рабочий процесс заключается в реализованном ими методе run.Здесь мы сначала рассмотрим реализацию метода Run в Acceptor:

//org.apache.tomcat.util.net.NioEndpoint.java
@Override
protected SocketChannel serverSocketAccept() throws Exception {
    return serverSock.accept();
}
//org.apache.tomcat.util.net.Acceptor.java
public class Acceptor<U> implements Runnable {

    private static final Log log = LogFactory.getLog(Acceptor.class);
    private static final StringManager sm = StringManager.getManager(Acceptor.class);

    private static final int INITIAL_ERROR_DELAY = 50;
    private static final int MAX_ERROR_DELAY = 1600;

    private final AbstractEndpoint<?,U> endpoint;
    private String threadName;
    protected volatile AcceptorState state = AcceptorState.NEW;


    public Acceptor(AbstractEndpoint<?,U> endpoint) {
        this.endpoint = endpoint;
    }


    public final AcceptorState getState() {
        return state;
    }


    final void setThreadName(final String threadName) {
        this.threadName = threadName;
    }


    final String getThreadName() {
        return threadName;
    }


    @Override
    public void run() {

        int errorDelay = 0;

        // Loop until we receive a shutdown command
        while (endpoint.isRunning()) {

            // Loop if endpoint is paused
            while (endpoint.isPaused() && endpoint.isRunning()) {
                state = AcceptorState.PAUSED;
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    // Ignore
                }
            }

            if (!endpoint.isRunning()) {
                break;
            }
            state = AcceptorState.RUNNING;

            try {
                //if we have reached max connections, wait
                endpoint.countUpOrAwaitConnection();

                // Endpoint might have been paused while waiting for latch
                // If that is the case, don't accept new connections
                if (endpoint.isPaused()) {
                    continue;
                }

                U socket = null;
                try {
                    // Accept the next incoming connection from the server
                    // socket
                    // 创建一个socketChannel,接收下一个从服务器进来的连接  
                    socket = endpoint.serverSocketAccept();
                } catch (Exception ioe) {
                    // We didn't get a socket
                    endpoint.countDownConnection();
                    if (endpoint.isRunning()) {
                        // Introduce delay if necessary
                        errorDelay = handleExceptionWithDelay(errorDelay);
                        // re-throw
                        throw ioe;
                    } else {
                        break;
                    }
                }
                // Successful accept, reset the error delay
                errorDelay = 0;

                // Configure the socket
                // 如果EndPoint处于running状态并且没有没暂停
                if (endpoint.isRunning() && !endpoint.isPaused()) {
                    // setSocketOptions() will hand the socket off to
                    // an appropriate processor if successful
                    if (!endpoint.setSocketOptions(socket)) {
                        endpoint.closeSocket(socket);
                    }
                } else {
                    endpoint.destroySocket(socket);
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                String msg = sm.getString("endpoint.accept.fail");
                // APR specific.
                // Could push this down but not sure it is worth the trouble.
                if (t instanceof Error) {
                    Error e = (Error) t;
                    if (e.getError() == 233) {
                        // Not an error on HP-UX so log as a warning
                        // so it can be filtered out on that platform
                        // See bug 50273
                        log.warn(msg, t);
                    } else {
                        log.error(msg, t);
                    }
                } else {
                        log.error(msg, t);
                }
            }
        }
        state = AcceptorState.ENDED;
    }


    ...

    public enum AcceptorState {
        NEW, RUNNING, PAUSED, ENDED
    }
}

Как вы можете видеть из приведенного выше метода запуска,AcceptorиспользоватьserverSock.accept()Заблокированный порт прослушивания, если есть входящее соединение, получить егоsocketEndPointВ обычном режиме звонитеNioEndPointизsetSocketOptionsметод, дляsetSocketOptionsкороче, на основеsocketпостроитьNioChannel, а затем поместите этоNioChannelзарегистрироваться наPollerвнутри списка событий подождитеpollerопрос:

/**
* org.apache.tomcat.util.net.NioEndpoint.java
* Process the specified connection.
* 处理指定的连接
* @param socket The socket channel
* @return <code>true</code> if the socket was correctly configured
*  and processing may continue, <code>false</code> if the socket needs to be
*  close immediately
* 如果socket配置正确,并且可能会继续处理,返回true 
* 如果socket需要立即关闭,则返回false
*/
@Override
protected boolean setSocketOptions(SocketChannel socket) {
    // Process the connection
    try {
        //disable blocking, APR style, we are gonna be polling it
        socket.configureBlocking(false);
        Socket sock = socket.socket();
        socketProperties.setProperties(sock);
    //从缓存中拿一个nioChannel  若没有,则创建一个。将socket传进去
        NioChannel channel = nioChannels.pop();
        if (channel == null) {
            SocketBufferHandler bufhandler = new SocketBufferHandler(
                    socketProperties.getAppReadBufSize(),
                    socketProperties.getAppWriteBufSize(),
                    socketProperties.getDirectBuffer());
            if (isSSLEnabled()) {
                channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
            } else {
                channel = new NioChannel(socket, bufhandler);
            }
        } else {
            channel.setIOChannel(socket);
            channel.reset();
        }
    //从pollers数组中获取一个Poller对象,注册这个nioChannel
        getPoller0().register(channel);
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        try {
            log.error(sm.getString("endpoint.socketOptionsError"), t);
        } catch (Throwable tt) {
            ExceptionUtils.handleThrowable(tt);
        }
        // Tell to close the socket
        return false;
    }
    return true;
}

/**
* Return an available poller in true round robin fashion.
*
* @return The next poller in sequence
*/
public Poller getPoller0() {
    int idx = Math.abs(pollerRotater.incrementAndGet()) % pollers.length;
    return pollers[idx];
}

оgetPoller0(), По умолчанию, как видно спереди, в массиве поллеров всего один элемент, который следует отметить. Давайте посмотрим на метод регистрации, реализованный Poller в NioEndPoint.Главное, зарегистрировать только что созданный сокет в Poller.

/**
* Registers a newly created socket with the poller.
*
* @param socket    The newly created socket
*/
public void register(final NioChannel socket) {
    socket.setPoller(this);
    NioSocketWrapper ka = new NioSocketWrapper(socket, NioEndpoint.this);
    socket.setSocketWrapper(ka);
    ka.setPoller(this);
    ka.setReadTimeout(getConnectionTimeout());
    ka.setWriteTimeout(getConnectionTimeout());
    ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
    ka.setSecure(isSSLEnabled());
    //从缓存中取出一个PollerEvent对象,若没有则创建一个。将socket和NioSocketWrapper设置进去
    PollerEvent r = eventCache.pop();
    ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
    if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
    else r.reset(socket,ka,OP_REGISTER);
    //添到到该Poller的事件列表
    addEvent(r);
}

Подводя итог описанному выше процессу:

Получив запрос от Акцептора, он делает следующее:

  • Подождите, если будет достигнуто максимальное количество подключений. В противном случае заблокируйте порт прослушивания.
  • При обнаружении соединения создается socketChannel. Если служба работает нормально, сокет передается соответствующему обработчику. В случае успеха сокет будет закрыт.

Здесь метод setSocketOptions NioEndPoint вызывается для обработки указанного соединения:

  • установить сокет в неблокирующий
  • Получите nioChannel из кэша, если нет, создайте его. Вставьте розетку.
  • Получите объект Poller из массива pollers и зарегистрируйте nioChannel с помощью Poller.

Последним шагом процесса регистрации является вызов метода register() Poller:

  • Создайте NioSocketWrapper, который обертывает сокет. Затем настройте связанные свойства, установите дляinterestOps значение SelectionKey.OP_READ.
  • Получите объект PollerEvent из кеша или создайте его, если он не существует. Инициализация или сброс этого объекта Event установит для егоinterestOps значение OP_REGISTER (используется опросчиком).
  • Добавьте новое событие PollerEvent в список событий этого опросчика, ожидая опроса потока опросчика.

Как работают опросы

Как видно из вышеизложенного, поллер также реализует интерфейс Runnable, создает соответствующие рабочие потоки и объединяет их во время выполнения этой части жизненного цикла запуска, поэтому давайте посмотрим на механизм его работы через его метод запуска. .

На самом деле, выше упоминался процесс регистрации Poller события в очереди событий. Следующее, что должен сделать поток Poller, — это как обрабатывать эти события.

Poller опрашивает события очереди событий в методе run, регистрирует интересOps SocketChannel в каждом PollerEvent в Selector, а затем удаляет PollerEvent из очереди. После этого SocketChannel выполняет неблокирующее чтение и запись данных посредством планирования Selector.

 /**
     * Poller class.
     */
    public class Poller implements Runnable {

        private Selector selector;
        private final SynchronizedQueue<PollerEvent> events =
                new SynchronizedQueue<>();

        private volatile boolean close = false;
        private long nextExpiration = 0;//optimize expiration handling

        private AtomicLong wakeupCounter = new AtomicLong(0);

        private volatile int keyCount = 0;

        public Poller() throws IOException {
            this.selector = Selector.open();
        }

        public int getKeyCount() { return keyCount; }

        public Selector getSelector() { return selector;}

      

        /**
         * The background thread that adds sockets to the Poller, checks the
         * poller for triggered events and hands the associated socket off to an
         * appropriate processor as events occur.
         */
        @Override
        public void run() {
            // Loop until destroy() is called
             // 循环直到 destroy() 被调用
            while (true) {

                boolean hasEvents = false;

                try {
                    if (!close) {
                        //遍历events,将每个事件中的Channel的interestOps注册到Selector中
                        hasEvents = events();
                        if (wakeupCounter.getAndSet(-1) > 0) {
                            //if we are here, means we have other stuff to do
                            //do a non blocking select
                            //如果走到了这里,代表已经有就绪的IO Channel
                            //调用非阻塞的select方法,直接返回就绪Channel的数量
                            keyCount = selector.selectNow();
                        } else {
                            //阻塞等待操作系统返回 数据已经就绪的Channel,然后被唤醒
                            keyCount = selector.select(selectorTimeout);
                        }
                        wakeupCounter.set(0);
                    }
                    if (close) {
                        events();
                        timeout(0, false);
                        try {
                            selector.close();
                        } catch (IOException ioe) {
                            log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
                        }
                        break;
                    }
                } catch (Throwable x) {
                    ExceptionUtils.handleThrowable(x);
                    log.error(sm.getString("endpoint.nio.selectorLoopError"), x);
                    continue;
                }
                //either we timed out or we woke up, process events first
                //如果上面select方法超时,或者被唤醒,先将events队列中的Channel注册到Selector上。
                if ( keyCount == 0 ) hasEvents = (hasEvents | events());

                Iterator<SelectionKey> iterator =
                    keyCount > 0 ? selector.selectedKeys().iterator() : null;
                // Walk through the collection of ready keys and dispatch
                // any active event.
                 // 遍历已就绪的Channel,并调用processKey来处理该Socket的IO。
                while (iterator != null && iterator.hasNext()) {
                    SelectionKey sk = iterator.next();
                    NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
                    // Attachment may be null if another thread has called
                    // cancelledKey()
                    // 如果其它线程已调用,则Attachment可能为空
                    if (attachment == null) {
                        iterator.remove();
                    } else {
                        iterator.remove();
                    //创建一个SocketProcessor,放入Tomcat线程池去执行
                        processKey(sk, attachment);
                    }
                }//while

                //process timeouts
                timeout(keyCount,hasEvents);
            }//while

            getStopLatch().countDown();
        }
        ...
    }

Вышеупомянутая часть чтения готового канала является очень распространенным использованием Java NIO, то есть селектор вызывает selectedKeys(), получает канал, чьи данные ввода-вывода готовы, проходит и вызывает метод processKey для обработки каждого события готовности канала. Метод processKey создаст SocketProcessor, а затем передаст его в пул потоков Tomcat для выполнения.

Еще один момент, на который следует обратить внимание, заключается в том, что метод events() используется для обработки события PollerEvent, выполнения PollerEvent.run() и последующего сброса PollerEvent в кэш для повторного использования объекта.

/**
* Processes events in the event queue of the Poller.
*
* @return <code>true</code> if some events were processed,
*   <code>false</code> if queue was empty
*/
public boolean events() {
    boolean result = false;

    PollerEvent pe = null;
    for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
        result = true;
        try {
            //把SocketChannel的interestOps注册到Selector中
            pe.run();
            pe.reset();
            if (running && !paused) {
                eventCache.push(pe);
            }
        } catch ( Throwable x ) {
            log.error(sm.getString("endpoint.nio.pollerEventError"), x);
        }
    }

    return result;
}

Поэтому метод PollerEvent.run() находится в центре нашего внимания:

/**
* PollerEvent, cacheable object for poller events to avoid GC
*/
public static class PollerEvent implements Runnable {

    private NioChannel socket;
    private int interestOps;
    private NioSocketWrapper socketWrapper;

    public PollerEvent(NioChannel ch, NioSocketWrapper w, int intOps) {
        reset(ch, w, intOps);
    }

    public void reset(NioChannel ch, NioSocketWrapper w, int intOps) {
        socket = ch;
        interestOps = intOps;
        socketWrapper = w;
    }

    public void reset() {
        reset(null, null, 0);
    }

    @Override
    public void run() {
        //Acceptor调用Poller.register()方法时,创建的PollerEvent的interestOps为OP_REGISTER,因此走这个分支
        if (interestOps == OP_REGISTER) {
            try {
                socket.getIOChannel().register(
                        socket.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper);
            } catch (Exception x) {
                log.error(sm.getString("endpoint.nio.registerFail"), x);
            }
        } else {
            final SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
            try {
                if (key == null) {
                    // The key was cancelled (e.g. due to socket closure)
                    // and removed from the selector while it was being
                    // processed. Count down the connections at this point
                    // since it won't have been counted down when the socket
                    // closed.
                    socket.socketWrapper.getEndpoint().countDownConnection();
                    ((NioSocketWrapper) socket.socketWrapper).closed = true;
                } else {
                    final NioSocketWrapper socketWrapper = (NioSocketWrapper) key.attachment();
                    if (socketWrapper != null) {
                        //we are registering the key to start with, reset the fairness counter.
                        int ops = key.interestOps() | interestOps;
                        socketWrapper.interestOps(ops);
                        key.interestOps(ops);
                    } else {
                        socket.getPoller().cancelledKey(key);
                    }
                }
            } catch (CancelledKeyException ckx) {
                try {
                    socket.getPoller().cancelledKey(key);
                } catch (Exception ignore) {}
            }
        }
    }

    @Override
    public String toString() {
        return "Poller event: socket [" + socket + "], socketWrapper [" + socketWrapper +
                "], interestOps [" + interestOps + "]";
    }
}

Пока что мы можем видеть роль потока опроса.

  • Зарегистрировать запрос, полученный акцептором, в очередь событий опросчика.
  • Опросчик опрашивает очередь событий, обрабатывает входящие события и регистрирует канал в PollerEvent в селекторе опросчика.
  • Опросите готовые каналы, создайте SocketProcessor для каждого готового канала и передайте его в пул потоков Tomcat для обработки.

Осталось то, как SocketProcessor адаптирует данные, запрошенные клиентом, а затем как передать их контейнеру Servlet для обработки.

То есть последний вызов в методе запуска ПоллераprocessKey(sk, attachment);:

protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {
    try {
        if ( close ) {
            cancelledKey(sk);
        } else if ( sk.isValid() && attachment != null ) {
            if (sk.isReadable() || sk.isWritable() ) {
                if ( attachment.getSendfileData() != null ) {
                    processSendfile(sk,attachment, false);
                } else {
                    unreg(sk, attachment, sk.readyOps());
                    boolean closeSocket = false;
                    // Read goes before write
                    if (sk.isReadable()) {
                        if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {
                            closeSocket = true;
                        }
                    }
                    if (!closeSocket && sk.isWritable()) {
                        if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {
                            closeSocket = true;
                        }
                    }
                    if (closeSocket) {
                        cancelledKey(sk);
                    }
                }
            }
        } else {
            //invalid key
            cancelledKey(sk);
        }
    } catch ( CancelledKeyException ckx ) {
        cancelledKey(sk);
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        log.error(sm.getString("endpoint.nio.keyProcessingError"), t);
    }
}

то есть изprocessSocketбудет использоваться в этом методеSocketProcessorдля обработки запроса:

/**
    * Process the given SocketWrapper with the given status. Used to trigger
    * processing as if the Poller (for those endpoints that have one)
    * selected the socket.
    *
    * @param socketWrapper The socket wrapper to process
    * @param event         The socket event to be processed
    * @param dispatch      Should the processing be performed on a new
    *                          container thread
    *
    * @return if processing was triggered successfully
    */
public boolean processSocket(SocketWrapperBase<S> socketWrapper,
        SocketEvent event, boolean dispatch) {
    try {
        if (socketWrapper == null) {
            return false;
        }
        SocketProcessorBase<S> sc = processorCache.pop();
        if (sc == null) {
            sc = createSocketProcessor(socketWrapper, event);
        } else {
            sc.reset(socketWrapper, event);
        }
        Executor executor = getExecutor();
        if (dispatch && executor != null) {
            executor.execute(sc);
        } else {
            sc.run();
        }
    } catch (RejectedExecutionException ree) {
        getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
        return false;
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        // This means we got an OOM or similar creating a thread, or that
        // the pool and its queue are full
        getLog().error(sm.getString("endpoint.process.fail"), t);
        return false;
    }
    return true;
}

SocketProcessor обрабатывает запрос

Кратко упомянуть здесьSocketProcessorПроцесс обработки помогает вам подключиться к контейнеру сервлетов для обработки. Из вышеизложенного мы можем знать, что конкретная обработка запроса выполняется SocketProcessor через пул потоков, Здесь давайте посмотрим на диаграмму последовательности выполнения запроса:

Как видно из рисунка,SocketProcessorпрошедшийHttp11ConnectionHandler, получитьHtpp11Processor,ПотомHtpp11ProcessorпозвонюprepareRequestспособ подготовки данных запроса. Тогда позвониCoyoteAdapterизserviceметодrequestиresponse, а затем передатьTomcatемкость для обработки.

Следующий процесс представлен серией вызовов:

connector.getService().getContainer().getPipeline().getFirst().invoke(request,response);

Здесь Служба сначала получается из Коннектора (Коннектор установил себя в CoyoteAdapter при создании CoyoteAdapter в методе initInternal), затем из Службы получается Контейнер, затем конвейер, а затем первое Значение конвейер и, наконец, вызовите метод вызова для выполнения запроса. Контейнер верхнего уровня хранится в службе. Когда вызывается метод вызова конвейера контейнера верхнего уровня, конвейер будет вызывать метод вызова значения в конвейере каждого слоя контейнеров слой за слоем, пока не будет BaseValue (StandardWrapperValve) в конвейере Wrapper, наконец, вызывается для обработки фильтров и сервлетов.

После того, как запрос передан в контейнер Tomcat для обработки, запрос затем передается в Engine, Host, Context и Wrapper слой за слоем. Наконец, после серии фильтров, он переходит к сервлету для выполнения нашего собственного конкретного логика кода.

Пока что некоторые вещи о Connector почти задействованы.Если у вас есть энергия на будущее, продолжайте изучать остальное, а затем поделитесь интерпретацией Webflux.

Пополнить: Благодаря Zero Degree (блог:www.jiangxinlingdu.com), здесь я дополню свое понимание некоторых дополнительных вопросов:

вот для чегоNioEndpointУчастие в реализации соответствующей части жизненного циклаinitServerSocket()Обратим внимание на детали:

// Separated out to make it easier for folks that extend NioEndpoint to
// implement custom [server]sockets
protected void initServerSocket() throws Exception {
    if (!getUseInheritedChannel()) {
        serverSock = ServerSocketChannel.open();
        socketProperties.setProperties(serverSock.socket());
        InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
        serverSock.socket().bind(addr,getAcceptCount());
    } else {
        // Retrieve the channel provided by the OS
        Channel ic = System.inheritedChannel();
        if (ic instanceof ServerSocketChannel) {
            serverSock = (ServerSocketChannel) ic;
        }
        if (serverSock == null) {
            throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
        }
    }
    serverSock.configureBlocking(true); //mimic APR behavior
}

Его последнее предложение, почему tomcat не устанавливает неблокировку? Это когда он только что инициализирован, он установлен в состояние блокировки, и блокировка блокируется только на этом потоке, т.е.AcceptorПри выполнении своего метода запуска в потоке он вызоветendpoint.serverSocketAccept()создатьsocketChannel, чтобы получить следующее входящее соединение с сервера. После успешного получения повторите этоsocketДля настройки он вызоветendpoint.setSocketOptions(socket), внутри этого метода вызоветsocket.configureBlocking(false);, в это время он включитсяSocketChannelВ неблокирующем режиме ознакомьтесь с подробностями предыдущей статьи для конкретного кода.