Tomcat源碼解析系列(十一)ProtocolHandler

前言
上篇文章中講到了 Connector 的初始化與啓動,其中最關鍵的就是 ProtocolHandler 的初始化與啓動。tomcat 中 ProtocolHandler 的默認實現類是 Http11NioProtocol。tomcat9.0.16 中 ProtocolHandler 的實現類中還有一個 Http11Nio2Protocol,二者實現上相似。這兩個實現的的父類都是 AbstractHttp11JsseProtocol,AbstractHttp11JsseProtocol 的父類是 AbstractHttp11Protocol,AbstractHttp11Protocol 的父類是 AbstractProtocol。segmentfault


1. Http11NioProtocol 構造方法
在 Connector 的構造方法中,用反射建立了一個 Http11NioProtocol 對象。tomcat

public Http11NioProtocol() {
    super(new NioEndpoint());
}
public AbstractHttp11JsseProtocol(AbstractJsseEndpoint<S,?> endpoint) {
    super(endpoint);
}
public AbstractHttp11Protocol(AbstractEndpoint<S,?> endpoint) {
    super(endpoint);
    setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
    ConnectionHandler<S> 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<S,?> endpoint;

public AbstractProtocol(AbstractEndpoint<S,?> 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);
}

在 Http11NioProtocol 構造方法裏,建立了一個 NioEndpoint 對象,Http11Nio2Protocol 與 Http11NioProtocol 的區別主要在這裏,Http11Nio2Protocol 在構造方法裏建立的是 Nio2Endpoint 對象。這個 NioEndpoint 對象是很是重要的組件,它封裝了 tomcat 的線程模型,後面會單獨講解這個類,這裏先很少作描述。
在 AbstractProtocol 裏把這個 NioEndpoint 對象複製給內部的 Endpoint 類型的屬性,而後設置了一些 Endpoint 對象的兩個屬性,setConnectionLinger 和 setTcpNoDelay 方法就是調用 Endpoint 對象的相關方法。app

在 Http11NioProtocol 父類的父類 AbstractHttp11Protocol 裏,建立了一個 ConnectionHandler 對象,並調用 setHandler(cHandler) 把這個對象賦值給本身 handler 屬性,這個屬性在 AbstractHttp11Protocol 的父類 AbstractProtocol 裏。dom

private Handler<S> handler;

protected void setHandler(Handler<S> handler) {
    this.handler = handler;
}

ConnectionHandler 是 AbstractProtocol 類的一個靜態內部類,其聲明爲async

rotected static class ConnectionHandler<S> implements AbstractEndpoint.Handler<S>

能夠看出 Handler 類是 AbstractEndpoint 裏的一個靜態的接口類型,這個 Handler 接口定義了一些處理 Socket 事件的方法。
AbstractHttp11Protocol 構造方法裏接着調用了 getEndpoint().setHandler(cHandler),把 ConnectionHandler 對象賦值 NioEndpoint 的 Handler 類型的屬性。tcp


2. ProtocolHandler#init 方法
ProtocolHandler 定義了 init 和 start 方法,ProtocolHandler 的實現類 AbstractProtocol 及其子類 AbstractHttp11Protocol 實現或重載了 init 方法,AbstractHttp11Protocol 的子類都沒有重載 init 方法。ide

2.1. AbstractHttp11Protocol#initui

/**
 * The upgrade protocol instances configured.
 */
private final List<UpgradeProtocol> 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();
}

AbstractHttp11Protocol#init 方法比較簡單,就是簡單調用了 configureUpgradeProtocol 方法。this

/**
 * The protocols that are available via internal Tomcat support for access
 * via HTTP upgrade.
 */
private final Map<String,UpgradeProtocol> httpUpgradeProtocols = new HashMap<>();
/**
 * The protocols that are available via internal Tomcat support for access
 * via ALPN negotiation.
 */
private final Map<String,UpgradeProtocol> 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()));
            }
        }
    }
}

configureUpgradeProtocol 方法也挺簡單的,就是將 UpgradeProtocol 放在 httpUpgradeProtocols 和 negotiatedProtocols 裏。線程

2.2. AbstractProtocol#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();
}

AbstractProtocol#init 裏前面幾行是註冊一些對象到 MBeanServer 裏,最重要的是最後一行的 endpoint.init(),這一行調用了 NioEndpoint 的 init 方法。關於 NioEndpoint 的詳細內容將在後續的文章裏講解。


3. ProtocolHandler#start 方法
ProtocolHandler 的實現類 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(),而後使用線程池來按期調用 startAsyncTimeout() 方法。
這裏的 getUtilityExecutor() 返回的對象,是在 Connector 的 initInternal 中調用 ProtocolHandler#setUtilityExecutor 設值的,傳入的對象 StandardServer 的 utilityExecutorWrapper,這個在以前介紹 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);
    }
}

能夠看出 startAsyncTimeout 方法的做用是按期調用 waitingProcessors 裏的 Processor 對象的 timeoutAsync 方法來處理一些超時的請求。
Processor 也是 tomcat 用來處理請求的一個關鍵組件。上文中提到的 ConnectionHandler 就是使用 Processor 來具體處理請求的。Processor 將會在後續的文章中介紹。


小結本文介紹了 ProtocolHandler 的初始化和啓動,ProtocolHandler 的默認實現類是 Http11NioProtocol。Http11NioProtocol 有一個很是重要的 NioEndpoint 對象,ProtocolHandler 的 init 和 start 方法中最關鍵的就是調用這個 NioEndpoint 對象的 init 和 start 方法。此外,在 AbstractHttp11Protocol 構造方法裏建立了一個也是很是重要的 ConnectionHandler 對象,這個對象是用來處理請求,ConnectionHandler 使用 Processor 對象來具體處理請求。

相關文章
相關標籤/搜索