Tomcatソース解析シリーズ(11)ProtoclHandler

9264 ワード

前文前の文章では、Connectorの初期化と起動について言及していますが、最も重要なのはProtocol Handlerの初期化と起動です。tomcatのProtocol Handlerのデフォルトの実装クラスはHttp 11 Nio Protocolです。tomcat 9.0.16のProtocol Handlerの実装クラスの中にもう一つのHttp 11 Nio 2 Protocolがあり、両者は類似を実現している。この二つはAbstractHttp 11 JsseProtocolであり、AbstractHttp 11 JsseProtocolの親はAbstractHttp 11 Protocolである。
1.Http 11 Nio Protocol構造方法Connectorの構造方法において、反射でHttp 11 Nio Protocolオブジェクトを作成しました。
public Http11NioProtocol() {
    super(new NioEndpoint());
}
public AbstractHttp11JsseProtocol(AbstractJsseEndpoint endpoint) {
    super(endpoint);
}
public AbstractHttp11Protocol(AbstractEndpoint endpoint) {
    super(endpoint);
    setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
    ConnectionHandler cHandler = new ConnectionHandler<>(this);
    setHandler(cHandler);
    getEndpoint().setHandler(cHandler);
}
/**
 * Endpoint that provides low-level network I/O - must be matched to the
 * ProtocolHandler implementation (ProtocolHandler using NIO, requires NIO
 * Endpoint etc.).
 */
private final AbstractEndpoint endpoint;

public AbstractProtocol(AbstractEndpoint endpoint) {
    this.endpoint = endpoint;
    setConnectionLinger(Constants.DEFAULT_CONNECTION_LINGER);
    setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}

public void setConnectionLinger(int connectionLinger) {
    endpoint.setConnectionLinger(connectionLinger);
}

public void setTcpNoDelay(boolean tcpNoDelay) {
    endpoint.setTcpNoDelay(tcpNoDelay);
}
Http 11 Nio Protocol構造方法において、NioEnd pointオブジェクトを作成しました。Http 11 Nio 2 ProtocolとHttp 11 Nio Protocolの違いは主にここにあります。このNioEndpointオブジェクトは非常に重要なコンポーネントです。tomcatのスレッドモデルをカプセル化しています。後でこのクラスを単独で説明します。ここでは説明を多くしません。AbstractProtocolでは、このNioEndpointオブジェクトを内部のEndpointタイプの属性にコピーして、いくつかのEndpointオブジェクトの2つの属性を設定しています。setConnection LinkerとsetTcpNoDelay方法は、Endpointオブジェクトを呼び出す方法です。
Http 11 NioProtocolの親類のAbstractHttp 11 Protocolでは、Connection Handlerオブジェクトを作成し、set Handler(cHandler)を呼び出して、このオブジェクトを自分のhander属性に割り当てます。この属性はAbstractHttp 11 Protocolの親類AbstractactProtocol colです。
private Handler handler;

protected void setHandler(Handler handler) {
    this.handler = handler;
}
Connection Handlerは、AbstractProtocolクラスの静的な内部クラスであり、声明では
rotected static class ConnectionHandler implements AbstractEndpoint.Handler
Handler類はAbstract Endpointの静的なインターフェースタイプであり、このHandlerインターフェースはSocketイベントを処理する方法を定義している。AbstractHttp 11 Protocol構造方法では、引き続きget Endpoint().set Handlerを呼び出し、Connection HandlerオブジェクトをNioEnd pointのHandlerタイプの属性に割り当てます。
2.ProtoclHandler Protocol Handlerはinitとstart方法を定義しています。Protocol Handlerの実現類AbstractProtoctocolおよびそのサブクラスAbstractHttp 11 Protocolはinit方法を実現したり、リロードしたりしました。AbstractHttp 11 Protoctocolのサブクラスもリロード方法がありません。
2.1.AbstractHttp 11 Protocol葃init

/**
 * The upgrade protocol instances configured.
 */
private final List upgradeProtocols = new ArrayList<>();


@Override
public void init() throws Exception {
    // Upgrade protocols have to be configured first since the endpoint
    // init (triggered via super.init() below) uses this list to configure
    // the list of ALPN protocols to advertise
    for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
        configureUpgradeProtocol(upgradeProtocol);
    }

    super.init();
}
AbstractHttp 11 Protocolメソッドは比較的簡単で、configreUpgrade Protocolメソッドを簡単に呼び出しました。
/**
 * The protocols that are available via internal Tomcat support for access
 * via HTTP upgrade.
 */
private final Map httpUpgradeProtocols = new HashMap<>();
/**
 * The protocols that are available via internal Tomcat support for access
 * via ALPN negotiation.
 */
private final Map negotiatedProtocols = new HashMap<>();
private void configureUpgradeProtocol(UpgradeProtocol upgradeProtocol) {
    // HTTP Upgrade
    String httpUpgradeName = upgradeProtocol.getHttpUpgradeName(getEndpoint().isSSLEnabled());
    boolean httpUpgradeConfigured = false;
    if (httpUpgradeName != null && httpUpgradeName.length() > 0) {
        httpUpgradeProtocols.put(httpUpgradeName, upgradeProtocol);
        httpUpgradeConfigured = true;
        getLog().info(sm.getString("abstractHttp11Protocol.httpUpgradeConfigured",
                getName(), httpUpgradeName));
    }


    // ALPN
    String alpnName = upgradeProtocol.getAlpnName();
    if (alpnName != null && alpnName.length() > 0) {
        if (getEndpoint().isAlpnSupported()) {
            negotiatedProtocols.put(alpnName, upgradeProtocol);
            getEndpoint().addNegotiatedProtocol(alpnName);
            getLog().info(sm.getString("abstractHttp11Protocol.alpnConfigured",
                    getName(), alpnName));
        } else {
            if (!httpUpgradeConfigured) {
                // ALPN is not supported by this connector and the upgrade
                // protocol implementation does not support standard HTTP
                // upgrade so there is no way available to enable support
                // for this protocol.
                getLog().error(sm.getString("abstractHttp11Protocol.alpnWithNoAlpn",
                        upgradeProtocol.getClass().getName(), alpnName, getName()));
            }
        }
    }
}
configreUpgradeProtocol方法も簡単です。つまり、httpUpgradeProtocolとnegotiatocolsの中に置いてあります。
2.2.Abstract Protocol铂init
@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();
}
Abstraact Protocol铉initの前の何行かはMBeanServerに登録されています。一番重要なのは最後の一行のendpoint.initです。この行はNioEnd pointのinit方法を呼び出しています。NioEntendpointについての詳細は後の文章で解説します。
3.Protocol Handler Protocol Handlerの実現類AbstractProtocolがstart方法を実現しました。AbstractProtocolのサブクラスはstart方法を再搭載していません。
@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);
}
AbstractProtocol↉startでは、まずendpoint.start()を呼び出してから、スレッド池を使って定期的にstartArync Timeout()を呼び出します。ここでのget Utility Exectorは、ConnectorのinitInternalでProtocol Handlerを呼び出します。これは、以前にServerの記事で紹介したものです。endpoint.startは後の文章で説明します。
protected void startAsyncTimeout() {
    if (asyncTimeoutFuture == null || (asyncTimeoutFuture != null && asyncTimeoutFuture.isDone())) {
        if (asyncTimeoutFuture != null && asyncTimeoutFuture.isDone()) {
            // There was an error executing the scheduled task, get it and log it
            try {
                asyncTimeoutFuture.get();
            } catch (InterruptedException | ExecutionException e) {
                getLog().error(sm.getString("abstractProtocolHandler.asyncTimeoutError"), e);
            }
        }
        asyncTimeoutFuture = getUtilityExecutor().scheduleAtFixedRate(
                new Runnable() {
                    @Override
                    public void run() {
                        long now = System.currentTimeMillis();
                        for (Processor processor : waitingProcessors) {
                            processor.timeoutAsync(now);
                        }
                    }
                }, 1, 1, TimeUnit.SECONDS);
    }
}
startArync Timeout方法の役割は、waitingProcessors内のProcessorオブジェクトを定期的に呼び出しているtimeout Arync方法で、タイムアウトの要求を処理するというものであることが分かります。Processorもtomcatが要求を処理するためのキーコンポーネントである。前述のConnection Handlerは、要求をProcessorを使用して具体的に処理するものである。Processorは次の文章で紹介します。
本稿ではProtocol Handlerの初期化と起動を紹介します。Protocol Handlerのデフォルトの実装クラスはHttp 11 Nio Protocolです。Http 11 NioProtocolには非常に重要なNioEnd pointオブジェクトがあります。Protocol Handlerのinitとstart方法の中で最も重要なのは、このNioEnd pointオブジェクトを呼び出すinitとstart方法です。また、AbstractHttp 11 Protocol構造方法においても非常に重要なConnection Handlerオブジェクトを作成しました。このオブジェクトは要求を処理するために使用され、Connection HandlerはProcessorオブジェクトを使用して要求を具体的に処理します。